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

3

我正在对这个问题中的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 提供的时间更加准确,应该用它而不是 time 命令。我正确吗?另外,“Total time”(3.57秒或3.62秒)中哪个应该使用?
最后,有没有关于这种测量的一般建议/最佳实践?我知道有些软件包可以让我们对程序进行基准测试,但我主要想手动进行测量(或使用脚本代替我进行测量)。
另外:运行时间是运行程序三次的中位数。
1个回答

4
我会在单核时间中使用-N1。我相信这也会限制GC只使用一个内核(这似乎适合基准测试,不是吗?),但其他人可能知道得更多。
至于您的第二个问题,在Haskell中进行基准测试的答案几乎总是使用criterion。Criterion将允许您计时程序的一个运行,并且您可以将其包装在脚本中,该脚本使用-N1-N2等运行程序。取3次运行的中位数作为非常快速和粗略的指标还可以,但如果您想依赖结果,则需要比那更多的运行。Criterion运行您的代码足够并执行适当的统计以给出合理的平均时间,以及置信区间和标准差(并尝试纠正您的机器有多忙)。我知道您问过如何自行执行最佳实践,但Criterion已经体现了很多:使用时钟时间,进行大量基准测试,并且如您所意识到的那样,不要仅仅采用简单的结果平均值。
如果要对整个程序进行基准测试,则Criterion所需的更改非常少。添加以下内容:
import Criterion.Main

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

其中oldMain是你原来主函数的名称。


我希望你意识到顺序时间和单核心时间(使用标志“-N1”和“-threaded”获得)之间存在差异。我需要绝对时间(顺序时间/ n个核心上的并行时间)和相对时间(1个核心上的并行时间/n个核心上的并行时间)加速度。我的问题是关于选择哪个时间作为顺序运行时间?应该编译代码时使用/不使用“-threaded”? - vis
如果我们不使用“-threaded”,就不能使用“-Nx”,但如果我们这样做,我们可以省略“-Nx”。我想知道程序是否会在所有核心上运行,还是默认为顺序执行(我有点怀疑,因为我们已经使用了“-threaded”标志进行编译)。 - vis
1
使用-threaded且不使用-Nx参数时,GHC默认使用“明智”的-Nx值。如果我没记错的话,这里的明智值是核心数减一;尽管在我不注意的时候可能会有所改变。 - wren romano
这对我来说很有意义。有人可以确认一下它是否已经改变了吗? - vis
好的,在双核机器上,如果我在没有使用“-threaded”选项的情况下进行编译,并打印出“numCapabilities”,输出值为1(显然)。当我在有“-threaded”标志的情况下,但不指定“-Nx”时,输出值为1(有趣。因此,ghc似乎将其解释为“-N1”)。当我指定“-Nx”时,输出值为“x”。现在,在具有更多核心的机器上,这种行为可能不会相同... - vis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接