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

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

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


person vis    schedule 08.07.2011    source источник


Ответы (1)


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

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

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

import Criterion.Main

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

где oldMain — это ваша основная функция.

person Neil Brown    schedule 08.07.2011
comment
Надеюсь, вы понимаете, что есть разница между последовательным временем и временем, затрачиваемым на одно ядро ​​(полученное с помощью флага -N1 при включенном -threaded). Мне нужны как абсолютные (последовательное время/параллельное время на n ядрах), так и относительное (параллельное время на 1 ядре/параллельное время на n ядрах) ускорения. Мой вопрос о том, какое время взять для последовательного запуска? Должно ли это быть путем компиляции кода с/без -threaded? - person vis; 08.07.2011
comment
Если мы не используем -threaded, мы не можем использовать -Nx, но если мы используем, мы можем опустить -Nx. Интересно, запускается ли программа затем на всех ядрах или по умолчанию работает последовательно (в чем я немного сомневаюсь, поскольку мы скомпилировали с флагом -threaded). - person vis; 08.07.2011
comment
С -threaded и без -Nx GHC по умолчанию использует разумное значение -Nx. IIRC, разумное значение здесь — это количество ядер минус один; хотя чувства могли измениться, когда я не смотрел. - person wren romano; 23.07.2011
comment
имеет смысл для меня. кто-нибудь может подтвердить это и что он не изменился? - person vis; 23.07.2011
comment
хорошо, на двухъядерной машине, если я скомпилирую без -threaded и напечатаю numCapabilities, вывод будет 1 (очевидно). С включенным флагом -threaded и без указания -Nx вывод равен 1 (интересно, поэтому ghc, кажется, интерпретирует его как -N1). Когда я указываю -Nx, вывод будет x. Теперь это поведение может быть другим на машинах с большим количеством ядер... - person vis; 23.07.2011