如何测量 Haskell 程序的顺序和并行运行时间

发布于 2024-11-18 20:23:00 字数 2780 浏览 0 评论 0原文

我正在从这个 问题 测量 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

两个我 2024-11-25 20:23:00

我会在单核时间使用 -N1 。我相信这也限制了 GC 使用一个核心(我认为这似乎适合基准测试?),但其他人可能知道更多。

至于你的第二个问题,Haskell 基准测试的答案几乎总是使用 criterion。 Criterion 将允许您对程序的一次运行进行计时,然后您可以将其包装在一个脚本中,该脚本使用 -N1-N2 等运行程序。 3 次运行的中位数作为一个非常快速且粗略的指标是可以的,但如果您想依赖结果,那么您将需要比这更多的运行次数。 Criterion 足以运行您的代码并执行适当的统计,为您提供合理的平均时间,以及置信区间和标准差(并且它会尝试纠正您的机器的繁忙程度)。我知道您询问过自己做这件事的最佳实践,但 Criterion 已经体现了很多内容:使用时钟时间,进行大量基准测试,并且正如您所意识到的,不要只对结果采取简单的平均值。

如果您想对整个程序进行基准测试,则 Criterion 只需要对您的程序进行很少的更改。添加以下内容:

import Criterion.Main

main :: IO ()
main = defaultMain [bench "My program" oldMain]

其中 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:

import Criterion.Main

main :: IO ()
main = defaultMain [bench "My program" oldMain]

where oldMain is whatever your main function used to be.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文