gat*_*ado 7 linux performance haskell
我想要一个能编写序列的程序,
1
...
10000000
Run Code Online (Sandbox Code Playgroud)
到一个文件.什么是最简单的代码可以编写,并获得不错的性能?我的直觉是存在一些缺乏缓冲的问题.我的C代码以100 MB/s的速度运行,而通过引用,Linux命令行实用程序dd以9 GB/s 3 GB/s的速度运行(对不起,请注意 - 请注意 - 我对大图订单更感兴趣 - 虽然很高).
人们会认为这将是一个现在解决的问题...即任何现代编译器都会立即编写这样的程序,表现得相当好......
#include <stdio.h>
int main(int argc, char **argv) {
int len = 10000000;
for (int a = 1; a <= len; a++) {
printf ("%d\n", a);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我正在编译clang -O3.调用putchar('\n')8次的性能框架可获得相当的性能.
一个naiive Haskell实现以13 MiB/sec的速度运行,并使用ghc -O2 -optc-O3 -optc-ffast-math -fllvm -fforce-recomp -funbox-strict-fields.(我没有重新编译我的库-fllvm,也许我需要这样做.)代码:
import Control.Monad
main = forM [1..10000000 :: Int] $ \j -> putStrLn (show j)
Run Code Online (Sandbox Code Playgroud)
我最好用Haskell刺伤的速度更慢,达到17 MiB/sec.问题是我找不到一个很好的方法来转换Vector成ByteStrings(也许有一个使用iteratees的解决方案?).
import qualified Data.Vector.Unboxed as V
import Data.Vector.Unboxed (Vector, Unbox, (!))
writeVector :: (Unbox a, Show a) => Vector a -> IO ()
writeVector v = V.mapM_ (System.IO.putStrLn . show) v
main = writeVector (V.generate 10000000 id)
Run Code Online (Sandbox Code Playgroud)
看起来编写ByteString很快,正如这段代码所示,编写相同数量的字符,
import Data.ByteString.Char8 as B
main = B.putStrLn (B.replicate 76000000 '\n')
Run Code Online (Sandbox Code Playgroud)
它的速度为1.3 GB/s,速度不是很快dd,但显然要好得多.
所有程序都使用默认优化级别编译(对于gcc为-O3,对于GHC为-O2)并运行
time ./prog > outfile
Run Code Online (Sandbox Code Playgroud)
作为基准,C程序需要1.07秒才能生成~76MB(78888897字节)的文件,吞吐量大约为70MB/s.
forM [1 .. 10000000] $ \j -> putStrLn (show j))耗时8.64秒,大约8.8MB/s.forM_而不是forM花了5.64s,大约13.5MB/s.ByteString从dflemstr的回答版本了9.13s,约8.3MB /秒.Text从dflemstr的回答版本了5.64s,约13.5MB /秒.Vector问题的版本耗时5.54秒,约为13.7MB/s.main = mapM_ (C.putStrLn . C.pack . show) $ [1 :: Int .. 10000000],其中C是Data.ByteString.Char8,花了4.25s,约17.9MB /秒.putStr . unlines . map show $ [1 :: Int .. 10000000] 花了3.06s,大约24.8MB/s.手动循环,
main = putStr $ go 1
where
go :: Int -> String
go i
| i > 10000000 = ""
| otherwise = shows i . showChar '\n' $ go (i+1)
Run Code Online (Sandbox Code Playgroud)
耗时2.32秒,约32.75MB/s.
main = putStrLn $ replicate 78888896 'a' 花了1.15秒,大约66MB/s.main = C.putStrLn $ C.replicate 78888896 'a'其中C是Data.ByteString.Char8,把0.143s,约530MB/s的,大致为懒惰同一附图ByteString秒.首先,不要使用forM或mapM除非你真的想收集结果.表现,这很糟糕.
然后,ByteString输出可以非常快(10.),但是如果ByteString输出的结构很慢(3.),则最终的代码比天真String输出慢.
3有什么可怕的?好吧,所有涉及String的都很短.所以你得到一份清单
Chunk "1234567" Empty
Run Code Online (Sandbox Code Playgroud)
并且在任何两个这样的之间Chunk "\n" Empty放置a,然后将结果列表连接起来,这意味着Empty当... (Chunk "1234567" (Chunk "\n" (Chunk "1234568" (...))))构建a时所有这些都被抛弃.这是很多浪费的构造 - 解构 - 重建正在进行中.可以通过严格的s和使用(以及换行)来实现Text与固定的"天真" String版本相当的速度.通过消除昂贵的单例,可以获得比6更好的性能.如果你将换行符粘贴到s上,而不是使用,则连接必须处理一半稍长的s,这会得到回报.packByteStringfromChunksData.List.intersperseString\k -> shows k "\n"showByteString
我对文本或向量的内部不够熟悉,提供的不仅仅是关于观察到的性能的原因的半教育猜测,所以我将它们排除在外.可以说,与固定的天真String版本相比,性能增益最多是微不足道的.
现在,6表明ByteString输出比String输出快,足以在这种情况下额外的pack输入工作得到补偿.但是,不要因为相信总是这样而被愚弄.如果String包装很长,则包装可能比String输出需要更多时间.
但千万调用putStrLn,无论是String或ByteString版本,采取了很多的时间.抓取stdout Handle一次并String在非IO代码中构造输出更快.unlines已经做得很好,但我们仍然受到列表建设的影响map show [1 .. 10^7].不幸的是,编译器没有设法消除它(但它消除了[1 .. 10^7],这已经相当不错).所以让我们自己做,导致8.这不是太可怕,但仍然需要C程序的两倍多.
可以通过低级别直接填充ByteStrings而不通过Stringvia 来制作更快的Haskell程序show,但我不知道C速度是否可达.无论如何,那个低级别的代码并不是很漂亮,所以我会饶恕你所拥有的东西,但是如果速度很重要,有时必须得到一个人的手.
| 归档时间: |
|
| 查看次数: |
496 次 |
| 最近记录: |