Как измерить последовательное и параллельное время выполнения программы на Haskell - PullRequest
3 голосов
/ 08 июля 2011

Я измеряю программу 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. Я прав? Кроме того, какое из «общего времени» (3,57 с или 3,62 с) следует использовать?

И, наконец, какой-нибудь общий совет / хорошая практика при проведении таких измерений? Я знаю, что есть некоторые пакеты, которые позволяют нам тестировать нашу программу, но я в основном заинтересован в том, чтобы проводить измерения вручную (или использовать скрипт для этого).

Также: время выполнения - это медиана запуска программы 3 раза.

1 Ответ

4 голосов
/ 08 июля 2011

Я бы использовал -N1 для одноядерного времени. Я считаю, что это также заставляет GC использовать одно ядро ​​(которое кажется подходящим для теста, я думаю?), Но другие могут знать больше.

Что касается вашего второго вопроса, ответом на бенчмаркинг в Haskell почти всегда является использование критерия . Критерий позволит вам рассчитать время одного прогона программы, а затем вы можете обернуть его в сценарий, который запускает программу с -N1, -N2 и т. Д. Взять медиану из 3 прогонов - это очень быстро и грубо. индикатор, но если вы хотите положиться на результаты, то вам нужно гораздо больше прогонов, чем это. Criterion выполняет ваш код достаточно и выполняет соответствующую статистику, чтобы дать вам разумное среднее время, а также доверительные интервалы и стандартное отклонение (и он пытается исправить то, насколько занят ваш компьютер). Я знаю, что вы спрашивали о наилучшей практике, чтобы делать это самостоятельно, но Критерий уже воплощает в себе многое из этого: используйте время, много измеряйте, и, как вы поняли, не просто принимайте простое среднее значение результатов.

Критерий требует очень мало изменений в вашей программе, если вы хотите сравнить все это. Добавьте это:

import Criterion.Main

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

где oldMain - это то, чем была ваша основная функция.

...