10

我正在使用标准来对我的 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)

这行得通,但我有两个担忧。首先,生成随机数据所需的时间是否包含在基准测试中?我发现了一个涉及该主题的问题,但老实说,我无法将其应用于我的代码。为了检查这是否发生,我编写了一个包含在 IO monad 中的数据生成器的替代版本。我将基准列表放在 main 中,称为生成器,用 <- 提取结果,然后将其传递给基准函数。我没有看到性能上的差异。

我的第二个问题与生成随机数据有关。现在生成器一旦创建就不会更新,这会导致在一次运行中生成相同的数据。这不是一个主要问题,但是如果能正确地完成它会很好。是否有一种巧妙的方法可以在每个 data* 函数中生成不同的随机数据?“整洁”的意思是“不让数据函数在 IO 内获取 StdGen”?

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

4

2 回答 2

7

这行得通,但我有两个担忧。首先,生成随机数据所需的时间是否包含在基准测试中?

是的。所有的随机生成都应该是惰性发生的。

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

这是预期的(如果我明白你的意思);randoms gen在需要它们之前(即在您的基准循环中),不会生成随机值。

是否有一种巧妙的方法可以在每个 data* 函数中生成不同的随机数据?“整洁”的意思是“不让数据函数在 IO 内获取 StdGen”?

您需要使用您提供的整数种子,IO或者使用.StdGenmkStdGen

关于。您应该如何从基准测试中获取 pRNG 的主要问题,您应该能够在您的东西之前完全评估随机输入,例如:defaultMain (benchmarks g)evaluateforce

import Control.DeepSeq(force)
import Control.Exception(evaluate)
myBench g = do randInputEvaled <- evaluate $ force $ dataFun g
               defaultMain [
                    bench "MyFun" $ nf benchFun randInputEvaled
                    ...

whereforce将其参数评估为正常形式,但这仍然会延迟发生。因此,为了在外部对其进行评估,bench我们使用evaluate了单子排序。seq如果您想避免导入,您还可以执行诸如调用元组中每个列表的尾部等操作。

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

编辑:如果您想从 IO 获取数据,例如从磁盘读取数据,并且不希望将其混入您的基准测试中,则此方法也是一个好主意。

于 2012-10-15T13:37:37.350 回答
0

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

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

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

于 2012-10-15T19:11:36.633 回答