インテル® C++ コンパイラー 18.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);
}