如何为Criterion基准创建数据?

时间:2012-10-15 13:05:09

标签: haskell criterion

我使用criterion来对我的Haskell代码进行基准测试。我正在做一些繁重的计算,我需要随机数据。我写了这样的主要基准文件:

main :: IO ()
main = newStdGen >>= defaultMain . benchmarks

benchmarks :: RandomGen g => g -> [Benchmark]
benchmarks gen =
   [
     bgroup "Group"
     [
       bench "MyFun" $ nf benchFun (dataFun gen)
     ]
   ]

我在不同的模块中为它们保留基准和数据生成器:

benchFun :: ([Double], [Double]) -> [Double]
benchFun (ls, sig) = fun ls sig

dataFun :: RandomGen g => g -> ([Double], [Double])
dataFun gen = (take 5 $ randoms gen, take 1024 $ randoms gen)

这有效,但我有两个问题。首先,生成基准中包含的随机数据所需的时间是多少?我发现a question that touches on that subject但老实说,我无法将其应用到我的代码中。为了检查是否发生这种情况,我在IO monad中编写了一个替代版本的数据生成器。我在main中放置了基准测试列表,称为生成器,用< - 提取结果,然后将其传递给基准测试函数。我发现性能没有差异。

我的第二个问题与生成随机数据有关。现在,生成器一旦创建就不会更新,这会导致在一次运行中生成相同的数据。这不是一个主要问题,但尽管如此,做到这一点会很好。有没有一种巧妙的方法在每个数据*函数中生成不同的随机数据? “Neat”意味着“没有使数据功能在IO中获取StdGen”?

编辑:如下面的评论中所述,我并不关心数据随机性。对我来说重要的是,生成数据所需的时间不包括在基准测试中。

2 个答案:

答案 0 :(得分:7)

  

这有效,但我有两个问题。首先,生成基准中包含的随机数据所需的时间是什么?

是的。所有的随机生成都应该懒散地进行。

  

为了检查是否发生这种情况,我在IO monad中编写了一个替代版本的数据生成器。我在main中放置了基准测试列表,称为生成器,用< - 提取结果,然后将其传递给基准测试函数。我发现性能没有差异。

这是预期的(如果我明白你的意思);来自randoms gen的随机值不会被生成,直到需要它们为止(即在基准测试循环中)。

  

是否有一种巧妙的方法可以在每个数据*函数中生成不同的随机数据? "纯"意味着"没有使数据功能在IO"?

中获取StdGen

您需要位于IO或使用您提供的整数种子创建StdGen mkStdGen

重新。关于如何从基准测试中获取pRNG内容的主要问题,您应该能够在defaultMain (benchmarks g)之前完全评估随机输入,evaluate和{ {1}}喜欢:

force

其中import Control.DeepSeq(force) import Control.Exception(evaluate) myBench g = do randInputEvaled <- evaluate $ force $ dataFun g defaultMain [ bench "MyFun" $ nf benchFun randInputEvaled ... 将其参数评估为普通形式,但这仍然会延迟发生。因此,为了在force之外进行评估,我们使用bench来利用monadic排序。如果你想避免导入,你也可以在元组中每个列表的尾部调用evaluate等。

除非你需要在内存中保存大量的测试数据,否则这种情况应该可以正常工作。

编辑:如果您想从IO获取数据,这种方法也是一个好主意,例如从磁盘读取数据,并且不希望混入您的基准测试。< / p>

答案 1 :(得分:0)

您可以尝试从磁盘文件中读取随机数据。 (事实上​​,如果你使用的是类似Unix的操作系统,你甚至可以使用/dev/urandom。)

但是,根据您需要的数据量,I / O时间可能使计算时间相形见绌。这取决于您需要多少随机数据。

(例如,如果您的基准测试读取随机数并计算它们的总和,那么它将受I / O限制。如果您的基准读取一个随机数并根据该数字进行一些巨大的计算,那么I / O几乎没有增加任何开销。)