gpt4 book ai didi

performance - Haskell 中 hFlush 的高 CPU 使用率

转载 作者:行者123 更新时间:2023-12-04 02:55:42 26 4
gpt4 key购买 nike

我发现以下 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 使用率。但是这个解决方案是不可满足的,因为它不刷新。

谢谢。

已编辑

非常感谢您的帮助!通过将 sequence and repeat 更改为 replicateM_,运行时间从 14 秒减少到 3.8 秒。

但现在我有另一个问题。我问上面的问题是因为当我从上面的程序中删除 hFlush 时,尽管它使用 sequence 和 repeat 重复 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 即使在顺序和重复中也运行得很快?是否有关于此行为的任何引用?

谢谢。

最佳答案

在每次写入时调用 hFlush 似乎是错误的。

这个简单的更改,使用严格的字节串,forM_replicateM_ 而不是您的显式 sequence 和 block 缓冲,减少了 16.2 的运行时间秒到 0.3 秒

{-# LANGUAGE OverloadedStrings #-}
module Main where

import qualified Data.ByteString.Char8 as S
import Control.Monad
import System.IO

str = S.pack "FugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFuga\n"

main = replicateM_ 1000000 $ S.putStr str

虽然更惯用的方法是使用惰性字节串的单次写入,依靠字节串子系统来协调写入。

import qualified Data.ByteString.Char8 as S
import qualified Data.ByteString.Lazy.Char8 as L
import Control.Monad
import System.IO

str :: S.ByteString
str = S.pack "FugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFugaFuga\n"

main = L.putStr $ L.fromChunks (replicate 1000000 str)

性能略有提高(0.27 秒)

关于performance - Haskell 中 hFlush 的高 CPU 使用率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13363142/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com