インテル® C++ コンパイラー 19.0 デベロッパー・ガイドおよびリファレンス
ショートベクトル乱数ジェネレーター (SVRNG) ライブラリーの典型的な使用モデルは、標準 C++ やインテル® マス・カーネル・ライブラリー (インテル® MKL) のベクトル統計乱数ジェネレーターと同じです。
Windows* では、スタティック/ダイナミック・ライブラリーを明示的にリンクする必要があります (スタティック: libirng.lib、ダイナミック: libirngmd.lib)。Linux* および macOS* では、コンパイラー・ドライバーにより自動的にリンクされます。
次の例は、シード 777 で基本ジェネレーター・エンジン MT19937 を使用してランダム・ストリームの生成をします。このエンジンは 2 つの配列を生成します。1 つ目は、<a = 0.0、b = 4.0> の範囲の 1024 個の一様分布乱数で、コンパイラーによりベクトル化されるスカラー・ジェネレーター呼び出しによって生成されます。2 つ目は、16 要素のブロックから成る、引数 <平均 = 2.0、標準偏差 = 1.0> の 1024 個の正規分布乱数で、SIMD ベクトル実装の直接呼び出しによって生成されます。生成後、エンジンと分布を削除します。そして、エラーの有無を調べるため、ステータスをチェックします。この例は、指定した引数で 2 つの分布のサンプルの平均を計算することを目的としています。
#include <stdio.h> #include <svrng.h> int main( void ) { int i, st = SVRNG_STATUS_OK; double res1[1024], res2[1024]; double sum1 = 0, sum2 = 0; double mean1, mean2; svrng_engine_t engine; svrng_distribution_t distr1, distr2; /* mt19937 エンジンを作成 */ engine = svrng_new_mt19937_engine( 777 ); /* 一様分布を作成 */ distr1 = svrng_new_uniform_distribution_double( 0.0, 4.0 ); /* 正規分布を作成 */ distr2 = svrng_new_normal_distribution_double( 2.0, 1.0 ); /* スカラー・ジェネレーター呼び出し、コンパイラーによりベクトル化される */ #pragma ivdep #pragma vector always for( i = 0; i < 1024; i ++ ) { res1[i] = svrng_generate_double( engine, distr1 ); } /* SIMD ベクトル実装の直接呼び出し */ /* 16 パックド要素を生成 */ for( i = 0; i < 1024; i += 16 ) { *((svrng_double16_t*)(&res2[i])) = svrng_generate16_double( engine, distr2 ); } /* 平均値を計算 */ for( i = 0; i < 1024; i++ ) { sum1 += res1[i]; sum2 += res2[i]; } mean1 = sum1 / 1024.0; mean2 = sum2 / 1024.0; /* 結果を出力 */ printf( "Sample mean of uniform distribution = %f\n", mean1 ); printf( "Sample mean of normal distribution = %f\n", mean2 ); /*ステータスをチェック */ st = svrng_get_status(); if(st != SVRNG_STATUS_OK) { printf("FAILED: status error %d returned\n", st); } /* 分布を削除 */ svrng_delete_distribution( distr1 ); svrng_delete_distribution( distr2 ); /* エンジンを削除 */ svrng_delete_engine( engine ); return st; }
もう 1 つの例は、特定のエンジンで並列およびシーケンシャルに生成した場合に同じ乱数シーケンスの生成を保証する、"Skip-Ahead" 法を使用するアプローチを示します。rand0 エンジンを作成し、"Skip-Ahead" 法を利用して T スレッドにコピーします。各スレッドは、一様分布の符号なし整数型の乱数を N 個生成し、すべての乱数 (LEN=T*N) をシーケンシャル呼び出しと比較します。
#include <stdio.h> #include <stdint.h> #include <svrng.h> #define LEN 1024 #define T 8 #define N (LEN/T) int main( void ) { uint32_t seq_res[LEN+32], parallel_res[LEN+32]; svrng_engine_t seq_engine; svrng_engine_t parallel_engine[T]; int l, n, t, errs = 0, st = SVRNG_STATUS_OK; /* シーケンシャル・エンジンと分布を作成 */ seq_engine = svrng_new_rand0_engine( 777 ); /* "Skip-Ahead" 法 を使用して t*N オフセットで */ /* 既存のシーケンシャル・エンジンを新しい T 並列エンジンにコピー */ for( t = 0; t < T; t++ ) { int thr_offset = t*N; parallel_engine[t] = svrng_copy_engine( seq_engine ); parallel_engine[t] = \ svrng_skipahead_engine( parallel_engine[t], thr_offset ); } /* スカラー関数を使用するシーケンシャル・ループ (ベクトル化可能) */ #pragma ivdep #pragma vector always for( l = 0; l < LEN; l++ ) { seq_res[l] = svrng_generate_uint( seq_engine ); } /* SIMD ベクトル関数を使用する並列ループ */ /* 複数のスレッドに分配可能 */ for( t = 0; t < T; t++ ) { for( n = 0; n < N; n += 8 ) { *((svrng_uint8_t*)(&(parallel_res[t*N+n]))) = \ svrng_generate8_uint( parallel_engine[t] ); } } /* シーケンシャルと並列の結果を比較 */ for(l = 0; l < LEN; l++) { if( parallel_res[l] != seq_res[l]) { errs++; } } /* ステータスをチェック */ st = svrng_get_status(); /* 全体の結果を出力 */ if(st != SVRNG_STATUS_OK) { printf("FAILED: status error %d returned\n", st); } else if(errs) { printf("FAILED: %d skipahead errors\n", errs); } else { printf("PASSED\n"); } /* エンジンを削除 */ svrng_delete_engine( seq_engine ); for( t = 0; t < T; t++ ) { svrng_delete_engine(parallel_engine[t]); } return (errs-st); }