如何测量 Haskell 程序的顺序和并行运行时间
我正在从这个 问题 测量 haskell 程序,以生成下表包含运行时间和加速摘要,以便我可以绘制图表。
#Cores Runtimes Speedups
Absolute Relative
Seq ? .. ..
1 3.712 .. ..
2 1.646 .. ..
第一个问题
虽然 1 核和 2 核上的运行时是通过使用 -threaded
标志(下面的 [3] 和 [4])编译程序来获取的,但我不是确定连续的时间(下面的[1]或[2]):
- 应该是在没有
-threaded
标志的情况下编译获得的时间,还是 - 在打开该标志的情况下获得的 时间不指定任何数量的核心,即没有
-Nx
不使用 -threaded
标志进行编译
$ ghc --make -O2 test.hs
[1] $ time ./test ## number of core = 1
102334155
real 0m4.194s
user 0m0.015s
sys 0m0.046s
使用 -threaded
标志进行编译
$ ghc --make -O2 test.hs -threaded -rtsopts
[2] $ time ./test ## number of core = not sure?
102334155
real 0m3.547s
user 0m0.000s
sys 0m0.078s
[3] $ time ./test +RTS -N1 ## number of core = 1
102334155
real 0m3.712s
user 0m0.016s
sys 0m0.046s
[4] $ time ./test +RTS -N2 ## number of core = 2
102334155
real 0m1.646s
user 0m0.016s
sys 0m0.046s
第二个问题
从上面可以看出,我使用 time
命令来测量运行时间。我正在花“真正”的时间。但是,如果我在打开 -sstderr
标志的情况下运行程序,我会得到更详细的信息:
$ ghc --make -O2 test.hs -rtsopts
$ ./test +RTS -sstderr
102334155
862,804 bytes allocated in the heap
2,432 bytes copied during GC
26,204 bytes maximum residency (1 sample(s))
19,716 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Generation 0: 1 collections, 0 parallel, 0.00s, 0.00s elapsed
Generation 1: 1 collections, 0 parallel, 0.00s, 0.00s elapsed
INIT time 0.00s ( 0.00s elapsed)
MUT time 3.57s ( 3.62s elapsed)
GC time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 3.57s ( 3.62s elapsed)
%GC time 0.0% (0.0% elapsed)
Alloc rate 241,517 bytes per MUT second
Productivity 100.0% of total user, 98.6% of total elapsed
我相信 -sstderr
提供了更准确的时间,我应该使用它来代替时间
命令。我说得对吗?另外,我应该使用“总时间”中的哪一个(3.57 秒或 3.62 秒)?
最后,在进行这样的测量时有什么一般建议/良好实践吗?我知道有一些软件包允许我们对程序进行基准测试,但我主要感兴趣的是手动进行测量(或使用脚本为我做这件事)。
另外:运行时间是程序运行 3 次的中位数。
I am taking measurement of the haskell program from this question to produce the following table with runtimes and speedups summary so I can plot in a graph.
#Cores Runtimes Speedups
Absolute Relative
Seq ? .. ..
1 3.712 .. ..
2 1.646 .. ..
First question
While the runtimes on 1 and 2 cores are taken by compiling the program with the -threaded
flag on ([3] and [4] below), I am not sure which time to take for the sequential one ([1] or [2] below):
- should it be the time obtained by compiling without the
-threaded
flag, or - that obtained with the flag on but then NOT specifying any number of cores i.e. with no
-Nx
Compiling without -threaded
flag
$ ghc --make -O2 test.hs
[1] $ time ./test ## number of core = 1
102334155
real 0m4.194s
user 0m0.015s
sys 0m0.046s
Compiling with -threaded
flag
$ ghc --make -O2 test.hs -threaded -rtsopts
[2] $ time ./test ## number of core = not sure?
102334155
real 0m3.547s
user 0m0.000s
sys 0m0.078s
[3] $ time ./test +RTS -N1 ## number of core = 1
102334155
real 0m3.712s
user 0m0.016s
sys 0m0.046s
[4] $ time ./test +RTS -N2 ## number of core = 2
102334155
real 0m1.646s
user 0m0.016s
sys 0m0.046s
Second question
As can be seen from above, I am using the time
command to measure the runtimes. I am taking the 'real' time. But if I run the program with the -sstderr
flag on, I get more detailed information:
$ ghc --make -O2 test.hs -rtsopts
$ ./test +RTS -sstderr
102334155
862,804 bytes allocated in the heap
2,432 bytes copied during GC
26,204 bytes maximum residency (1 sample(s))
19,716 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Generation 0: 1 collections, 0 parallel, 0.00s, 0.00s elapsed
Generation 1: 1 collections, 0 parallel, 0.00s, 0.00s elapsed
INIT time 0.00s ( 0.00s elapsed)
MUT time 3.57s ( 3.62s elapsed)
GC time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 3.57s ( 3.62s elapsed)
%GC time 0.0% (0.0% elapsed)
Alloc rate 241,517 bytes per MUT second
Productivity 100.0% of total user, 98.6% of total elapsed
I believe that the -sstderr
provides a more accurate time which I should use instead of the time
command. Am I correct? Also, which of the 'Total time' (3.57s or 3.62s) should I use?
And finally, any general advice/good practice while taking measurements like this? I am aware that there are some packages that allow us to benchmark our program, but I am mainly interested in taking the measurements manually (or using a script to do that for me).
Also: the runtimes are the median of running the program 3 times.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我会在单核时间使用
-N1
。我相信这也限制了 GC 使用一个核心(我认为这似乎适合基准测试?),但其他人可能知道更多。至于你的第二个问题,Haskell 基准测试的答案几乎总是使用 criterion。 Criterion 将允许您对程序的一次运行进行计时,然后您可以将其包装在一个脚本中,该脚本使用
-N1
、-N2
等运行程序。 3 次运行的中位数作为一个非常快速且粗略的指标是可以的,但如果您想依赖结果,那么您将需要比这更多的运行次数。 Criterion 足以运行您的代码并执行适当的统计,为您提供合理的平均时间,以及置信区间和标准差(并且它会尝试纠正您的机器的繁忙程度)。我知道您询问过自己做这件事的最佳实践,但 Criterion 已经体现了很多内容:使用时钟时间,进行大量基准测试,并且正如您所意识到的,不要只对结果采取简单的平均值。如果您想对整个程序进行基准测试,则 Criterion 只需要对您的程序进行很少的更改。添加以下内容:
其中
oldMain
是您以前的主要函数。I would use
-N1
for the single-core time. I believe that also constrains the GC to use one core (which seems fitting for the benchmark, I think?), but others may know more.As for your second question, the answer to benchmarking in Haskell is nearly always to use criterion. Criterion will allow you to time one run of the program, and you can then wrap it in a script which runs the program with
-N1
,-N2
, etc. Taking the median of 3 runs is okay as a very quick and rough indicator, but if you want to rely on the results then you'll need a lot more runs than that. Criterion runs your code enough and performs the appropriate statistics to give you a sensible average time, as well as confidence intervals and standard deviation (and it tries to correct for how busy your machine is). I know you asked about best practice for doing it yourself, but Criterion already embodies a lot of it: use clock time, benchmark a lot, and as you realised, don't just take a simple mean of the results.Criterion requires very little change to your program if you want to benchmark the whole thing. Add this:
where
oldMain
is whatever your main function used to be.