Принудительная оценка ввода функции перед сравнительным анализом в Критерии

Как вы заставляете оценку ввода функции перед сравнительным анализом функции в Критерии? Я пытаюсь сравнить некоторые функции, но хотел бы исключить время для оценки ввода. В рассматриваемом коде для ввода используются распакованные векторы, которые нельзя использовать для векторов Int. Пример кода ниже:

-- V is Data.Vector.Unboxed
shortv = V.fromList [1..10] :: V.Vector GHC.Int.Int16
intv = V.fromList [1..10] :: V.Vector GHC.Int.Int32

main :: IO ()
main = defaultMain [
          bench "encode ShortV" $ whnf encodeInt16V shortv
          ,bench "encode IntV" $ whnf encodeInt32V intv
       ]

Критерий критериев включает в себя время для создания shortv и входы intv при сравнении вышеуказанных функций. Критерии измерений приведены ниже - она ​​измеряет около 400 нс для каждой функции, которая, по-видимому, включает время сборки для входов:

benchmarking encode ShortV
mean: 379.6917 ns, lb 378.0229 ns, ub 382.4529 ns, ci 0.950
std dev: 10.79084 ns, lb 7.360444 ns, ub 15.89614 ns, ci 0.950

benchmarking encode IntV
mean: 392.2736 ns, lb 391.2816 ns, ub 393.4853 ns, ci 0.950
std dev: 5.565134 ns, lb 4.694539 ns, ub 6.689224 ns, ci 0.950 

Теперь, если основной раздел кода теста изменен на нижеприведенное (путем удаления второй функции теста):

main = defaultMain [
          bench "encode ShortV" $ whnf encodeInt16V shortv
       ]

Ввод shortv, кажется, оценивается до того, как функция encodeInt16V будет протестирована. Это действительно желаемый вывод для меня, потому что этот тест измеряет время выполнения функции, исключая время для создания ввода. Критерий вывода ниже:

benchmarking encode ShortV
mean: 148.8488 ns, lb 148.4714 ns, ub 149.6279 ns, ci 0.950
std dev: 2.658834 ns, lb 1.621119 ns, ub 5.184792 ns, ci 0.950

Точно так же, если я использую только эталонный тест "закодировать IntV", я тоже получу ~150 нс времени.

Из документации Criterion я знаю, что она пытается избежать ленивых оценок для более точного бенчмаркинга. Это имеет смысл, и на самом деле это не проблема. Мой вопрос заключается в том, как мне построить входы shortv и intv, чтобы они уже были оценены перед передачей в стендовую функцию. Прямо сейчас я могу сделать это, ограничив defaultMain для тестирования только одной функции за раз (как я только что показал выше), но это не идеальное решение.

EDIT1

В тесте Criterion происходит нечто иное, и, похоже, это происходит только в массиве Vector, а не в списках. Если я форсирую полную оценку, печатая shortv и intv, тест все равно измеряет время как ~400 нс, а не ~150 нс. Обновление кода ниже:

main = do
  V.forM_ shortv $ \x -> do print x
  V.forM_ intv $ \x -> do print x
  defaultMain [
          bench "encode ShortV" $ whnf encodeInt16V shortv
          ,bench "encode IntV" $ whnf encodeInt32V intv
       ]

Выход критерия (также имеет 158,4% выбросов, что кажется неправильным):

estimating clock resolution...
mean is 5.121819 us (160001 iterations)
found 253488 outliers among 159999 samples (158.4%)
  126544 (79.1%) low severe
  126944 (79.3%) high severe
estimating cost of a clock call...
mean is 47.45021 ns (35 iterations)
found 5 outliers among 35 samples (14.3%)
  2 (5.7%) high mild
  3 (8.6%) high severe

benchmarking encode ShortV
mean: 382.1599 ns, lb 381.3501 ns, ub 383.0841 ns, ci 0.950
std dev: 4.409181 ns, lb 3.828800 ns, ub 5.216401 ns, ci 0.950

benchmarking encode IntV
mean: 394.0517 ns, lb 392.4718 ns, ub 396.7014 ns, ci 0.950
std dev: 10.20773 ns, lb 7.101707 ns, ub 17.53715 ns, ci 0.950

1 ответ

Решение

Вы могли бы использовать evaluate перед вызовом defaultMain для запуска тестов. Не уверен, что это самое чистое решение, но это будет выглядеть так:

main = do
  evaluate shortv
  evaluate intv
  defaultMain [..]
Другие вопросы по тегам