私のシステム:
システム仕様: Intel core2duo E4500 3700g メモリ L2 キャッシュ 2M x64 fedora 17
フロップ/ムフロップの測定方法
さて、私は papi ライブラリ (ハードウェア パフォーマンス カウンターを読み取るため) を使用して、コードのフロップと mフロップを測定します。リアルタイムの処理時間、フロップ、最後にフロップ/処理時間を返します。フロップと MFLOPS を含む最終結果を取得するための命令または浮動小数点演算と合計サイクル。
MY 計算カーネル
3 つのループ行列行列乗算 (正方行列) と 3 つのネストされたループを使用し、内部ループで 1 次元配列に対して何らかの操作を行いました。
最初のカーネル MM
float a[size][size];
float b[size][size];
float c[size][size];
start_calculate_MFlops();
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
for (int k = 0; k < size; **k+=1**) {
*c[i][j]=c[i][j]+a[i][k] * b[k][j];*
}
}
}
stop_calculate_MFlops();
1 次元配列を持つ 2 番目のカーネル
float d[size];
float e[size];
float f[size];
float g[size];
float r = 3.6541;
start_calculate_MFlops();
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
for (int k = 0; k < size; ++k) {
d[k]=d[k]+e[k]+f[k]+g[k]+r;
}
}
}
stop_calculate_MFlops();
フロップについて私が知っていること
行列行列乗算 (MM) は、その内側のループ (ここでは浮動小数点演算) で 2 つの演算を実行し、サイズ X に対して反復する 3 つのループがあるため、理論的には MM の合計フロップ数は 2*n^3 になります。
2 番目のカーネルには 3 つのループがあり、最も内側のループには計算を行う 1 次元配列があります。このループには 4 つの浮動小数点演算があります。したがって、理論的には 4*n^3 フロップの合計フロップがあります。
上記で計算したフロップは、実際のマシンで発生するものとまったく同じではないことはわかっています。実際のマシンでは、ロードやストアなどの他の操作があり、合計すると理論上のフロップになります。
質問 ?:
2番目のカーネルのように1次元配列を使用すると、理論上のフロップはコードを実行して測定することで得られるフロップと同じか、またはその前後です。 3 しかし、2 次元配列を使用する最初のカーネル MM を使用すると、理論上のフロップは 2n^3 ですが、コードを実行すると、測定値が理論値よりも高すぎます。行列乗算)*n^3+=6n^3. 以下のコードだけで、最も内側のループの行列乗算行を変更しました。
A[i][j]++;
3 つのネストされたループでのこのコードの理論上のフロップは 1 操作 * n^3=n^3 コードを実行すると、結果は予想よりも高すぎました。これは 2+(最も内側のループの 1 操作)*n でした。 ^3=3*n^3
サイズ 512X512 の行列のサンプル結果:
Real_time: 1.718368 Proc_time: 1.227672 合計 flpops: 807,107,072 MFLOPS: 657.429016
Real_time: 3.608078 Proc_time: 3.042272 合計 flpops: 807,024,448 MFLOPS: 265.270355
理論上のフロップ: 2*512*512*512= 268,435,456
測定されたフロップ = 6*512^3 = 807,107,072
3 つのネストされたループでの 1 次元配列操作のサンプル結果
Real_time: 1.282257 Proc_time: 1.155990 合計 flpops: 536,872,000 MFLOPS: 464.426117
理論上のフロップ: 4n^3 = 536,870,912
測定されたフロップ: 4n^3=4*512^3+オーバーヘッド(他の操作?)= 536,872,000
前述の動作の理由を見つけることができませんでしたか? 私の仮定は本当ですか?
以前の説明よりもはるかに簡単になることを願っています。
実用的とは、コードを実行して測定された実際のフロップを意味します。
コード:
void countFlops() {
int size = 512;
int itr = 20;
float a[size][size];
float b[size][size];
float c[size][size];
/* float d[size];
float e[size];
float f[size];
float g[size];*/
float r = 3.6541;
float real_time, proc_time, mflops;
long long flpops;
float ireal_time, iproc_time, imflops;
long long iflpops;
int retval;
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
a[j][j] = b[j][j] = c[j][j] = 1.0125;
}
}
/* for (int i = 0; i < size; ++i) {
d[i]=e[i]=f[i]=g[i]=10.235;
}*/
if ((retval = PAPI_flops(&ireal_time, &iproc_time, &iflpops, &imflops))
< PAPI_OK) {
printf("Could not initialise PAPI_flops \n");
printf("Your platform may not support floating point operation event.\n");
printf("retval: %d\n", retval);
exit(1);
}
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
for (int k = 0; k < size; k+=16) {
c[i][j]=c[i][j]+a[i][k] * b[k][j];
}
}
}
/* for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
for (int k = 0; k < size; ++k) {
d[k]=d[k]+e[k]+f[k]+g[k]+r;
}
}
}*/
if ((retval = PAPI_flops(&real_time, &proc_time, &flpops, &mflops))
< PAPI_OK) {
printf("retval: %d\n", retval);
exit(1);
}
string flpops_tmp;
flpops_tmp = output_formatted_string(flpops);
printf(
"calculation: Real_time: %f Proc_time: %f Total flpops: %s MFLOPS: %f\n",
real_time, proc_time, flpops_tmp.c_str(), mflops);
}
ありがとうございました