Как использовать Criterion для измерения производительности программ на Haskell? - PullRequest
15 голосов
/ 10 июля 2011

Я пытаюсь измерить производительность простой DWT-программы Haar с помощью платформы Criterion. (Это ошибочно медленно, но я оставлю это для другого вопроса). К сожалению, я не могу найти хорошую документацию в Интернете. Мои две основные проблемы

  • Как можно передавать данные из одного теста в другой? Я хочу, чтобы время каждого этапа программы.
  • Как работает выборка и избежать ленивых вычислений, повторно использующих свои предыдущие вычисления?

Этот источник относительно урезан; первая функция getRandList генерирует список случайных чисел; haarStep преобразует входной сигнал в разности и суммы, а haarDWT вызывает первый и возвращает значения. Я пытаюсь передать getRandList в haarDWT через ленивую оценку, но, возможно, мое использование неверно / не поддерживается. Сроки не имеют смысла.

{-# LANGUAGE ViewPatterns #-}

import Control.Arrow
import qualified Data.Vector.Unboxed as V
import System.Random
import Criterion.Main

invSqrt2 = 0.70710678118654752440

getRandList :: RandomGen g => g -> Int -> [Float]
getRandList gen 0 = []
getRandList gen n = v:rest where
    (v, gen') = random gen
    rest = getRandList gen' (n - 1)

haarStep :: V.Vector Float -> (V.Vector Float, V.Vector Float)
haarStep = (alternatingOp (-) &&& alternatingOp (+)) where
    alternatingOp op x = V.generate (V.length x `div` 2) (\i ->
        ((x V.! (2 * i)) `op` (x V.! (2 * i + 1))) * invSqrt2)

haarDWT :: V.Vector Float -> V.Vector Float
haarDWT xl@(V.length -> 1) = xl
haarDWT (haarStep -> (d, s)) = haarDWT s V.++ d

main = do
    gen <- getStdGen
    inData <- return $ getRandList gen 2097152
    outData <- return $ haarDWT (V.fromList inData)

    defaultMain [
        bench "get input" $ nf id inData,
        bench "transform" $ nf V.toList outData
        ]
    writeFile "input.dat" (unlines $ map show inData)
    writeFile "output.dat" (unlines $ map show $ V.toList outData)

Наконец, я получаю сообщение об ошибке при попытке вызвать его с помощью -s 1; может быть, это просто ошибка критерия.

Main: ./Data/Vector/Generic.hs:237 ((!)): index out of bounds (1,1)

Заранее спасибо!

1 Ответ

9 голосов
/ 10 июля 2011

Опубликованный тест является ошибочно медленным ... или это

Вы уверены, что это ошибочно? Ты касаешься (ну, звонок "nf" трогает) 2 миллиона элементов в штучной упаковке - это 4 миллиона указателей. Вы можете назвать это ошибочным, если хотите, но проблема в том, что вы думаете, что вы измеряете по сравнению с тем, что вы действительно измеряете.

Обмен данными между эталонами

Обмен данными может быть осуществлен посредством частичного применения. В моих тестах у меня обычно есть

let var = somethingCommon in
defaultMain [ bench "one" (nf (func1 somethingCommon) input1)
            , bench "two" (nf (func2 somethingCommon) input2)]

Избегать повторного использования в присутствии ленивых оценок

Критерий избегает совместного использования, разделяя вашу функцию и ваш вклад. У вас есть подписи, такие как:

funcToBenchmark :: (NFData b) => a -> b
inputForFunc :: a

В Haskell каждый раз, когда вы применяете funcToBenchmark inputForFunc, он создает thunk, который нуждается в оценке. Совместное использование невозможно, если вы не используете то же имя переменной, что и в предыдущих вычислениях. Автоматическое запоминание отсутствует - это, похоже, распространенное недоразумение.

Обратите внимание на нюанс в , что не передается. Мы не передаем окончательный результат, но input является общим. Если генерация ввода - это то, что вы хотите сравнить (например, getRandList, в данном случае), тогда сравните это, а не только функцию identity + nf:

main = do
    gen <- getStdGen
    let inData = getRandList gen size
        inVec = V.fromList inData
        size = 2097152
    defaultMain
      [ bench "get input for real" $ nf (getRandList gen) size
      , bench "get input for real and run harrDWT and listify a vector" $ nf (V.toList . haarDWT  . V.fromList . getRandList gen) size
      , bench "screw generation, how fast is haarDWT" $ whnf haarDWT inVec] -- for unboxed vectors whnf is sufficient

Интерпретация данных

Третий тест довольно поучителен. Давайте посмотрим, что выводит критерий:

benchmarking screw generation, how fast is haarDWT
collecting 100 samples, 1 iterations each, in estimated 137.3525 s
bootstrapping with 100000 resamples
mean: 134.7204 ms, lb 134.5117 ms, ub 135.0135 ms, ci 0.950

Основываясь на одном прогоне, Criterion считает, что для выполнения его 100 выборок потребуется 137 секунд. Примерно через десять секунд это было сделано - что случилось? Ну, первый запуск заставил все входы (inVec), что было дорого. Последующие прогоны нашли значение вместо thunk, и, таким образом, мы действительно отметили haarDWT, а не StdGen RNG (который, как известно, мучительно медленный).

...