【问题标题】:Creating multiple Criterion Benchmarks at once一次创建多个标准基准
【发布时间】:2018-11-16 12:27:57
【问题描述】:

这段代码编译运行没有问题:

module Main where

import Criterion.Main

main :: IO ()
main =
  defaultMain
    [env (return $ [1,2])
         (\is ->
            bgroup "group" (benchmarks is))]

timesTwo :: Int -> Int
timesTwo i = 2 * i

benchmarks :: [Int] -> [Benchmark]
benchmarks is = [ bench "foo" $ nf timesTwo (is !! 0)
                , bench "foo" $ nf timesTwo (is !! 1) ]

但是,如果我将 benchmarks 函数更改为如下所示

benchmarks :: [Int] -> [Benchmark]
benchmarks is = map (\i -> bench "foo" $ nf timesTwo i) is

它仍然可以编译,但我得到这个运行时错误:

ghci> main
*** Exception: Criterion atttempted to retrieve a non-existent environment!
        Perhaps you forgot to use lazy pattern matching in a function which
        constructs benchmarks from an environment?
        (see the documentation for `env` for details)

我该如何解决这个问题?

如您所见,我的目标是映射从环境中获得的列表,以便将其转换为可以与 Criterion 一起使用的 Benchmarks 列表。 p>

注意:我最终想要使用比两个更多的元素,所以元组不是我想要的。

【问题讨论】:

  • 如何从环境中获取列表?你会静态知道它的长度吗?
  • @oisdk 在实践中,我将在编译时使用我自己知道的数字。想一想:[100, 1000, 10000, 1000000] 我的目标是做一个“大 O”基准。 (如果您知道的话,请随意提出更好的“大 O”分析方法。)

标签: haskell criterion


【解决方案1】:

对于不同尺寸的基准测试,我通常会这样做:

module Main (main) where

import Criterion.Main
import System.Random
import Control.Monad

import qualified Data.List
import qualified Data.Sequence

int :: Int -> IO Int
int n = randomRIO (0,n)

benchAtSize :: Int -> Benchmark
benchAtSize n =
    env (replicateM n (int n)) $
    \xs ->
         bgroup (show n)
           [ bench "Data.List"     $ nf Data.List.sort xs
           , bench "Data.Sequence" $ nf (Data.Sequence.sort . Data.Sequence.fromList) xs
           ]

main :: IO ()
main = defaultMain (map benchAtSize [100, 1000, 10000])

env 有助于确保在同一个样本上比较两个不同的函数,并且它不是为了在运行基准测试之前计算整个数据集而设计的。此外,由于env 创建的所有数据在对其范围内的任何内容进行基准测试期间都保存在内存中,因此您希望尽可能减少它,以减少基准测试时的开销。

【讨论】:

    【解决方案2】:

    env 非常挑剔和严格。你不能在这里使用它。在env 下创建的基准测试的结构不能依赖于环境。也就是说,环境可以被被基准测试的代码使用,但基准测试本身的组织、命名等方式不能使用它。这是因为criterion 有时会通过_|_ 而不是真实环境,当它只想检查基准的结构而不执行它们时。当您使用!! 时,基准的组织是手动给出的,即使在is = _|_ 时也是完整的:

    benchmarks _|_ = [ bench "foo" $ nf timesTwo _|_ -- _|_ !! n = _|_; nf is not strict
                     , bench "foo" $ nf timesTwo _|_ ] -- "bench"s are still there
    

    但是map 打破了这一点:

    benchmarks _|_ = map _etc _|_
                   = case _|_ of -- definition of map
                          [] -> []
                          x:xs -> _etc x : map _etc xs
                   = _|_ -- benchmark structure gone
    

    你最好的选择就是不使用env

    main = do is <- _ -- however you calculate is
              defaultMain $ bgroup "group" $ benchmark is
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-28
      • 2017-12-31
      • 1970-01-01
      • 1970-01-01
      • 2020-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多