この記事は、
C++ (fork) Advent Calendar 2013 19日目の記事になります。
近々C++14が標準化されようという時期に、今更感が拭えませんが
C++11から、C言語由来のrand()の他に新しい乱数ライブラリが導入されたので。
自前の乱数ジェネレータを作ってみました。
この記事は、江添さんのブログを参考に書いています。
詳しい実装方法に付いては、江添さんのブログを読んでください、僕には説明できません。
本の虫
使い方自体は簡単で
乱数そのものを生成するengineクラスを、
乱数の範囲を調節するdistributionクラスに引数として渡すだけです
今回はこの std::mt19937 engine の代わり使えるクラスを実装します。
まずヘッダ定義
次に定義
C++11の<chrono>で取得したナノ秒単位の時刻を乱数の種に、
Xorshiftで擬似乱数を発生させるクラスになります。
コンストラクタで乱数の種を設定するので、手動で初期化する手間が省けます 。
高速が売りのXorshiftなので、ベンチマークを取ってみる。
ということで、標準の乱数ジェネレータと速度比較します。
乱数を10000回生成するのにかかった時間を比較するのですが、
単にループで回すだけだと、当然最適化で全部消されます。
最適化無しでの速度比較は、あんまり実体に即してない気がするのでやりません、
なので生成した乱数を配列に書き込み、
処理時間の出力時にランダム一つ出力することで、過度の最適化を回避します。
時間の単位はナノ秒です。
実行
ありゃりゃ
とまあ、3倍近い速度差が出てしまいました。
流石標準ライブラリは速いですね、
皆さんも、標準ライブラリにある機能は、自前で再実装せず標準ライブラリの機能を活用しましょう。
とまあ、ここで締めてもいいのですが、
カンの良い方なら気付いていると思いますが上の乱数ジェネレータが遅い理由は、
randam()が10回呼び出される度に、乱数の種の初期化を行うからです。
c++の<chrono>は地味に遅いのです。
なので速度が必要な場合、初期化の頻度を減らすか、消し去るかすれば大丈夫です。
こんな感じで初期化の頻度を落とすと、標準ライブラリのstd::mt19937より速くなります。
std::mt19937の擬似乱数アルゴリズムメルセンヌ・ツイスタなので。
Xorshiftを使う自作の乱数ジェネレータの方が速いのは当然ではありますけどね、
さて、
乱数ジェネレータの名前で感づいた人も多いのでしょうが、
このジェネレータの名前は
rdtsc_random_generatorです。
chronoでもなく、
high_resolution_clockでもなく、
rdtscです。
つまり
tscで初期化する環境依存しまくりのオレオレ乱数ジェネレータを記事のネタの為に書き換えたものです。
名前と実体が一致していませんが、今更クラスの名前を変えるのも面倒くさいので、
tsc版のソースも公開します。
std::mt19937より、ずっとはやい!!
ですが、環境依存しまくりの外法です、ご利用は計画的に、
インラインアセンブリを使っているので、x86とx64限定かつコンパイラにも依存します。
gccとclangでは動きましたがMSVCでは試してません。
まあ、途中で初期化するとか中途半端なことやらなければいい話です
門外漢の浅知恵なので有効かどうかは知りません、
追記、x,y,z,w全てが0にならないかぎり限り、何で初期化してもいいみたいです
それとstd::chrono::high_resolution_clock::now()はtscを直に読むより二桁ぐらい遅いみたいです。
追記、gccのchronoの実装を確認したところ、内部でシステムコールの
clock_gettime(CLOCK_REALTIME, &hoge)とか
syscall(SYS_clock_gettime, CLOCK_REALTIME, &hoge)あたりを呼び出しているらしい。
プロセス切替えのオーバヘッドが大きいいらしく、ナノ秒精度の結果を返すが、読み出しに1マイクロ秒程度かかる。
ついでなので、c++11標準の乱数ジェネレータの速度比較もします。
条件は今までと同様に、乱数を10000回生成するのにかかった時間で比較します。
時間の単位はマイクロ秒。使用したコンパイラはgccです。
処理時間の比較
乱数ジェネレータ |
処理時間 μs |
注釈 |
xorshift |
153 |
自作 |
mt19937 |
393 |
メルセンヌ・ツイスタ |
minstd_rand0 |
355 |
線形合同法 |
minstd_rand |
363 |
線形合同法の改良版 |
mt19937_64 |
384 |
メルセンヌ・ツイスタの64ビット版 |
ranlux24 |
1262 |
RANLUX法のレベル3 |
ranlux48 |
3907 |
RANLUX法のレベル4 |
knuth_b |
401 |
KnuthのリオーダーアルゴリズムB |
0 件のコメント:
コメントを投稿