【发布时间】:2019-07-20 13:00:56
【问题描述】:
在这个打印从 1 到 10000000 的所有数字的简单程序中,一个 Haskell 版本和一个 C 版本,为什么 Haskell 这么慢以及哪些命令有助于学习如何提高 Haskell 程序的性能强>?
以下是一份报告,其中包含重现我激动人心的事件所需的所有详细信息,制作报告时会打印包括 Makefile 的来源的来源:
$ make -B report
cat Foo.hs
import Data.Foldable
main = traverse_ print [1..10000000]
cat Fooc.c
#include <stdio.h>
int main()
{
for (int n = 0; n < 10000000; ++n)
{
printf("%d\n", n+1);
}
}
ghc -O3 Foo.hs -o Foo
time ./Foo | tail -n1
3.45user 0.03system 0:03.49elapsed 99%CPU (0avgtext+0avgdata 4092maxresident)k
0inputs+0outputs (0major+290minor)pagefaults 0swaps
10000000
cc -O3 Fooc.c -o Fooc
time ./Fooc | tail -n1
0.63user 0.02system 0:00.66elapsed 99%CPU (0avgtext+0avgdata 1468maxresident)k
0inputs+0outputs (0major+63minor)pagefaults 0swaps
10000000
cat Makefile
.PHONY: printFoo printFooc printMakefile
printFoo: Foo.hs
cat $^
printFooc: Fooc.c
cat $^
printMakefile: Makefile
cat $^
Fooc: CFLAGS=-O3
Fooc: Fooc.c
Foo: Foo.hs
ghc -O3 $^ -o $@
.PHONY: timeFoo timeFooc
timeFoo: Foo
time ./$^ | tail -n1
timeFooc: Fooc
time ./$^ | tail -n1
.PHONY: report
report: printFoo printFooc timeFoo timeFooc printMakefile
【问题讨论】:
-
请使用
Int64,或Int而不是Integer。 -
还请注意,您的两个计时都包括两个不同的东西:生成值的计算和打印它们的 I/O。如果您将它们分开,您的结果会提供更多信息。例如
print (last [1::Int .. 10000000])) 将只打印列表的最后一个元素(同时使用Int而不是Integer,正如其他人所建议的那样)。 -
人们,如果您要发表评论,请确保您确实有观点!
Integer与固定大小类型的对比与这里的性能问题无关。也没有未装箱的列表结构。事实上,print在 C 中似乎确实比printf慢得多。但是 a) 担心 printing 的性能是很愚蠢的,因为如果您有这么多数字,那么don't print them 很重要! (对于大量数据,始终使用二进制格式。) ... -
...因此说“Haskell 很慢”是非常不公平的,只是一个并不重要的特定任务很慢。 b) 这不是那么慢。因素 5,许多语言只能梦想接近 C - 在性能真正会很重要的任务中!根据经验,写得很好的 Haskell 比 C 慢 2 倍;有时,即使是一个简单的版本也接近于 C,只要付出一些努力,几乎总能实现。
-
我应该指出,这两个程序的编写经济成本相同,理解成本也相似,因此“您编写的代码很慢”的评论信息量不大。
标签: performance haskell