我发现以下 Haskell 代码使用 100% CPU 并在我的 Linux 服务器上完成大约需要 14 秒。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.ByteString.Lazy.Char8 as L
import System.IO
str = L.pack "FugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFuga\n"
main = do
hSetBuffering stdout (BlockBuffering (Just 1000))
sequence (take 1000000 (repeat (L.hPutStr stdout str >> hFlush stdout)))
return ()
另一方面,非常相似的 Python 代码在大约 3 秒内完成相同的任务。
import sys
str = "FugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFuga\n"
def main():
for i in xrange(0, 1000000):
print str,
sys.stdout.flush()
# doIO()
main()
通过使用 strace,我发现在 Haskell 版本中每次调用 hFlush 时都会调用 select。另一方面,在 Python 版本中不调用 select。我想这是 Haskell 版本慢的原因之一。
有什么方法可以提高 Haskell 版本的性能吗?
我已经尝试省略 hFlush,它确实大大降低了 CPU 使用率。但是这个解决方案不能令人满意,因为它不会冲洗。
谢谢。
已编辑
非常感谢您的帮助!通过将序列和重复更改为 replicateM_,运行时间从 14 秒减少到 3.8 秒。
但现在我有另一个问题。我问了上面的问题,因为当我从上面的程序中删除 hFlush 时,尽管它使用序列和重复重复 I/O,但它运行得很快。
为什么只有 sequence 和 hFlush 的组合使它变慢?
为了确认我的新问题,我改变了我的程序如下进行分析。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.ByteString.Char8 as S
import System.IO
import Control.Monad
str = S.pack "FugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFuga\n"
doIO = S.hPutStr stdout str >> hFlush stdout
doIO' = S.hPutStr stdout str >> hFlush stdout
doIOWithoutFlush = S.hPutStr stdout str
main = do
hSetBuffering stdout (BlockBuffering (Just 1000))
sequence (take 1000000 (repeat doIO))
replicateM_ 1000000 doIO'
sequence (take 1000000 (repeat doIOWithoutFlush))
return ()
通过编译运行如下:
$ ghc -O2 -prof -fprof-auto Fuga.hs
$ ./Fuga +RTS -p -RTS > /dev/null
我得到了以下结果。
COST CENTRE MODULE %time %alloc
doIO Main 74.7 35.8
doIO' Main 21.4 35.8
doIOWithoutFlush Main 2.6 21.4
main Main 1.3 6.9
执行相同任务的 doIO 和 doIO' 有什么区别?为什么 doIOWithoutFlush 即使按顺序重复运行也很快?有没有关于这种行为的参考?
谢谢。