如何通过缓冲提高Haskell IO的速度?

5

我读到《Real World Haskell》(第七章,第189页)中关于IO缓存的内容,并尝试测试不同的缓存大小对性能的影响。

import System.IO
import Data.Time.Clock
import Data.Char(toUpper)

main :: IO ()
main = do
  hInp <- openFile "bigFile.txt" ReadMode
  let bufferSize = truncate $ 2**10
  hSetBuffering hInp (BlockBuffering (Just bufferSize))
  bufferMode <- hGetBuffering hInp
  putStrLn $ "Current buffering mode: " ++ (show bufferMode)

  startTime <- getCurrentTime
  inp <- hGetContents hInp
  writeFile "processed.txt" (map toUpper inp)
  hClose hInp
  finishTime <- getCurrentTime
  print $ diffUTCTime finishTime startTime
  return ()

然后我创建了一个名为“bigFile.txt”的文件。

-rw-rw-r-- 1 user user 96M янв.  26 09:49 bigFile.txt

我需要运行我的程序来处理这个文件,并尝试不同的缓冲区大小:

Current buffering mode: BlockBuffering (Just 32)
9.744967s   

Current buffering mode: BlockBuffering (Just 1024)
9.667924s                                      

Current buffering mode: BlockBuffering (Just 1048576)
9.494807s    

Current buffering mode: BlockBuffering (Just 1073741824)
9.792453s   

但程序运行时间几乎相同。这是正常的,还是我做错了什么?

1个回答

11
在现代操作系统中,由于内核执行的预读以及文件可能已经存在页面缓存中(如果您最近已经读取了该文件),因此缓冲区大小对线性读取文件的影响很小。
以下是一个测量缓冲写入效果的程序。典型结果如下:
$ ./mkbigfile 32      -- 12.864733s
$ ./mkbigfile 64      --  9.668272s
$ ./mkbigfile 128     --  6.993664s
$ ./mkbigfile 512     --  4.130989s
$ ./mkbigfile 1024    --  3.536652s
$ ./mkbigfile 16384   --  3.055403s
$ ./mkbigfile 1000000 --  3.004879s

来源:

{-# LANGUAGE OverloadedStrings #-}

import qualified Data.ByteString as BS
import Data.ByteString (ByteString)
import Control.Monad
import System.IO
import System.Environment
import Data.Time.Clock

main = do
  (arg:_) <- getArgs
  let size = read arg
  let bs = "abcdefghijklmnopqrstuvwxyz"
      n = 96000000 `div` (length bs)
  h <- openFile "bigFile.txt" WriteMode
  hSetBuffering h (BlockBuffering (Just size))
  startTime <- getCurrentTime
  replicateM_ n $ hPutStrLn h bs
  hClose h
  finishTime <- getCurrentTime
  print $ diffUTCTime finishTime startTime
  return ()

那么为什么原帖作者在自己的程序中没有看到这种差异呢? - Carsten S
3
OP的程序设置了读取句柄的缓冲区,而不是写入句柄的。 - ErikR
即使内核有缓冲区,你仍然需要系统调用来读取它吗? - dfeuer
当然可以,但关键是添加另一层缓冲(这就是hSetBuffering控制的内容)对于读取操作并不一定会加快速度。 - ErikR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接