我使用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”?
编辑:如下面的评论中所述,我并不关心数据随机性。对我来说重要的是,生成数据所需的时间不包括在基准测试中。答案 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几乎没有增加任何开销。)