Haskell 在标准中进行基准测试之前强制评估功能输入

Haskell 在标准中进行基准测试之前强制评估功能输入,haskell,lazy-evaluation,criterion,Haskell,Lazy Evaluation,Criterion,在中对函数进行基准测试之前,如何强制对函数的输入进行评估?我试图对一些函数进行基准测试,但希望排除评估输入thunk的时间。所讨论的代码用于输入,不能对Int向量进行deepseq。下面的代码片段示例: -- 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 ()

在中对函数进行基准测试之前,如何强制对函数的输入进行评估?我试图对一些函数进行基准测试,但希望排除评估输入thunk的时间。所讨论的代码用于输入,不能对Int向量进行deepseq。下面的代码片段示例:

-- 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输入的时间。标准测量值如下-每个功能的测量值约为400ns,似乎还包括输入的构建时间:

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 
现在,如果基准代码的主要部分修改为以下内容(通过删除第二个工作台函数):

在对encodeInt16V功能进行基准测试之前,似乎对shortv输入进行了评估。这确实是我所期望的输出,因为这个基准测量函数执行的时间,不包括构建输入的时间。标准输出如下:

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
类似地,如果我只对“encodeintv”基准测试进行基准测试,那么这个基准测试的时间也会达到150ns

我从标准文档中了解到,它试图避免懒惰的评估,以实现更准确的基准测试。这是有道理的,在这里并不是一个真正的问题。我的问题是如何构建shortv和intv输入,以便在传递给bench函数之前对它们进行评估。现在,我可以通过限制defaultMain一次只对一个函数进行基准测试来实现这一点(正如我刚才所示),但这不是一个理想的解决方案

EDIT1

Criteria benchmark在这里还发生了一些事情,它似乎只发生在向量数组上,而不是列表上。如果我通过打印shortv和intv来强制进行完整的评估,那么基准测试的时间仍然是~400ns,而不是~150ns。代码更新如下:

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%的异常值,似乎不正确):

您可以在调用defaultMain之前使用。不确定它是否是最干净的解决方案,但它看起来是这样的:

main = do
  evaluate shortv
  evaluate intv
  defaultMain [..]

按照您的建议,使用evaluate进行了测试,但结果没有改变。我想这是因为它将表达式求值为whnf,而不是nf。我认为这不是问题所在:例如,
Int32
上的
Vector
是围绕
Data.Vector.Primitive.Vector Int32
的新类型,它只包含严格的
Int
s字段和
ByteArray
。最后一个是一个基本的
ByteArray#
数据类型,我认为这是严格的。但是测试应该很简单:只需将
evaluate
调用改为打印总和的调用。是的,你是对的。我在向量数组上尝试了forM_u和print语句,得到了相同的结果。还有其他一些事情在进行,这似乎非常特定于向量数组。当我最初编写带有列表的函数时,我没有看到这个问题。
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
main = do
  evaluate shortv
  evaluate intv
  defaultMain [..]