另一个微基准测试:为什么这个“循环”(用 ghc -O2 -fllvm
编译,7.4.1,Linux 64bit 3.2 内核,重定向到 /dev/null
)
mapM_ print [1..100000000]
比普通的简单 for-cycle 慢约 5 倍
C
与 write(2)
非缓冲系统调用?我正在尝试收集 Haskell 问题。即使这种缓慢的 C 解决方案也比 Haskell 快得多
int i;
char buf[16];
for (i=0; i<=100000000; i++) {
sprintf(buf, "%d\n", i);
write(1, buf, strlen(buf));
}
最佳答案
好的,我的盒子上有 C 代码,根据 gcc -O3
编译运行大约需要 21.5 秒,原始 Haskell 代码大约需要 56 秒。所以不是 5 的因数,略高于 2.5。
第一个重要的区别是
mapM_ print [1..100000000]
用途
Integer
s,这有点慢,因为它涉及预先检查,然后使用盒装 Int
s,而 Show
Int
的实例对未装箱的 Int#
是否进行转换工作s。添加类型签名,以便 Haskell 代码适用于
Int
,mapM_ print [1 :: Int .. 100000000]
将时间缩短到 47 秒,比 C 代码花费的时间多一倍。
现在,另一个很大的区别是
show
产生 Char
的链表并且不只是填充连续的字节缓冲区。那也比较慢。然后是
Char
的链表s 用于填充字节缓冲区,然后将其写入 stdout
处理。所以,Haskell 代码比 C 代码做更多、更复杂的事情,因此它需要更长的时间也就不足为奇了。
诚然,希望有一种简单的方法来更直接地(因此更快)输出这些东西。但是,处理它的正确方法是使用更合适的算法(也适用于 C)。一个简单的改变
putStr . unlines $ map show [0 :: Int .. 100000000]
花费的时间几乎减少了一半,如果想要真的很快,可以使用更快的
ByteString
I/O 并有效地构建输出,如 applicative's answer 中所示.
关于haskell - Haskell 中的基本 I/O 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13334074/