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)

これは機能しますが、2 つの懸念事項があります。まず、ランダム データの生成に必要な時間はベンチマークに含まれていますか? その件に関する質問を見つけましたが、正直なところ、それを自分のコードに適用することはできません。これが発生するかどうかを確認するために、IO モナド内に含まれる別のバージョンのデータ ジェネレーターを作成しました。ジェネレーターと呼ばれる main でベンチマーク リストを配置し、<- で結果を抽出してから、ベンチマーク関数に渡しました。パフォーマンスに違いは見られませんでした。

2 つ目の懸念事項は、ランダム データの生成に関するものです。現在、一度作成されたジェネレーターは更新されていないため、1 回の実行で同じデータが生成されます。これは大きな問題ではありませんが、それでも適切に作成するとよいでしょう。各 data* 関数内で異なるランダム データを生成する適切な方法はありますか? 「Neat」とは、「IO内でStdGenを取得するデータ関数を作成しない」ことを意味しますか?

編集:以下のコメントで述べたように、データのランダム性についてはあまり気にしません。私にとって重要なのは、データの生成に必要な時間がベンチマークに含まれていないことです。

4

2 に答える 2

7

これは機能しますが、2つの懸念があります。まず、ランダムデータを生成するのに必要な時間はベンチマークに含まれていますか?

はい、そうです。ランダムな生成はすべて怠惰に行われるはずです。

これが発生するかどうかを確認するために、IOモナドで囲まれたデータジェネレータの代替バージョンを作成しました。ジェネレーターと呼ばれるmainを使用してベンチマークリストを配置し、<-を使用して結果を抽出し、それをベンチマーク関数に渡しました。パフォーマンスに違いは見られませんでした。

これは予想されることです(私があなたの意味を理解している場合)。からのランダムな値は、randoms gen必要になるまで(つまり、ベンチマークループ内で)生成されません。

各データ*関数内で異なるランダムデータを生成するためのきちんとした方法はありますか?「きちんとした」とは、「データ関数が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
                    ...

ここでforce、引数を通常の形式に評価しますが、これはまだ怠惰に起こります。したがって、外部で評価されるようにするために、モナディックシーケンスを活用benchするために使用します。インポートを避けたい場合は、タプル内の各リストの末尾をevaluate呼び出すなどのこともできます。seq

大量のテストデータをメモリに保持する必要がない限り、この種のことは問題なく機能するはずです。

編集:この方法は、ディスクからの読み取りなど、IOからデータを取得し、それをベンチマークに混ぜたくない場合にも適しています。

于 2012-10-15T13:37:37.350 に答える
0

代わりに、ディスク ファイルからランダム データを読み取ってみることができます。(実際、Unix ライクな OS を使用している場合は、 を使用することもできます/dev/urandom。)

ただし、必要なデータの量によっては、I/O 時間が計算時間よりも大きくなる場合があります。必要なランダムデータの量によって異なります。

(たとえば、ベンチマークが乱数を読み取り、その合計を計算する場合、I/O が制限されます。ベンチマークが乱数を読み取り、その 1 つの数値のみに基づいて巨大な計算を行う場合、I/O はほとんど追加しません。オーバーヘッドはまったくありません。)

于 2012-10-15T19:11:36.633 に答える