1

openMP を使用して以下のコードを最適化したい

double val;
double m_y = 0.0f;
double m_u = 0.0f;
double m_v = 0.0f;

#define _MSE(m, t) \
val = refData[t] - calData[t];  \
m += val*val; 

#pragma omp parallel 
 {
 #pragma omp for
for( i=0; i<(width*height)/2; i++ ) {  //yuv422: 2 pixels at a time
    _MSE(m_u, 0); 
    _MSE(m_y, 1); 
    _MSE(m_v, 2); 
    _MSE(m_y, 3); 

  #pragma omp reduction(+:refData) reduction(+:calData)
    refData += 4;
    calData += 4;
 // int id = omp_get_thread_num();
 //printf("Thread %d performed %d iterations of the loop\n",id ,i);
}

}

上記のコードを最適化するための提案を歓迎します。現在、出力が間違っています。

4

2 に答える 2

2

一番簡単なのは、4 つのスレッドに分割して、それぞれの UYVY エラーを計算することだと思います。それらを個別の値にする代わりに、それらを配列にします。

double sqError[4] = {0};
const int numBytes = width * height * 2;

#pragma omp parallel for
for( int elem = 0; elem < 4; elem++ ) {
    for( int i = elem; i < numBytes; i += 4 ) {
        int val = refData[i] - calData[i];
        sqError[elem] += (double)(val*val);
    }
}

このように、各スレッドは 1 つのものに対して排他的に動作し、競合は発生しません。

おそらく、これは OMP の最も高度な使用法ではないかもしれませんが、スピードアップが見られるはずです。


パフォーマンスの低下についてのコメントの後、いくつかの実験を行ったところ、実際にはパフォーマンスが低下していることがわかりました。これはキャッシュミスが原因であると思われます。

あなたが言った:

今回はopenMPでパフォーマンスヒット:時間:0.040637、シリアル時間:0.018670

そこで、各変数のリダクションと単一のループを使用して作り直しました。

    #pragma omp parallel for reduction(+:e0) reduction(+:e1) reduction(+:e2) reduction(+:e3)
    for( int i = 0; i < numBytes; i += 4 ) {
        int val = refData[i] - calData[i];
        e0 += (double)(val*val);
        val = refData[i+1] - calData[i+1];
        e1 += (double)(val*val);
        val = refData[i+2] - calData[i+2];
        e2 += (double)(val*val);
        val = refData[i+3] - calData[i+3];
        e3 += (double)(val*val);
    }

4 コア マシンでのテスト ケースでは、4 倍弱の改善が見られました。

serial:             2025 ms
omp with 2 loops:   6850 ms
omp with reduction: 455  ms

[編集]コードの最初の部分が非並列バージョンよりもパフォーマンスが悪かった理由について、Hristo Iliev氏は次のように述べています。

あなたの最初のコードは、マルチスレッド コードでの偽共有のひどい例です。sqError にはそれぞれ 8 バイトの要素が 4 つしかないため、1 つのキャッシュ ラインに収まります (最新の x86 CPU では半分のキャッシュ ラインでも)。4 つのスレッドが常に隣接する要素に書き込みを行っているため、偽共有による大量のコア間キャッシュの無効化が発生します。代わりにこのような構造を使用することで、これを回避できます struct _error { double val; ダブルパッド[7]; } sqError[4]; これで、各 sqError[i].val が個別のキャッシュ ラインに配置されるため、偽の共有がなくなります。

于 2013-02-11T02:24:26.117 に答える
0

コードは MSE を計算しているように見えますが、同じ合計にm. 並列処理が適切に機能するには、の共有を排除する必要があります。1 つのアプローチは、異なる合計またはsmを格納するためだけに、配列 (幅 * 高さ/2 だと思います) を事前に割り当てることです。m最後に、最後にすべての合計を合計します。

また、これが実際に高速であることをテストしてください。

于 2013-02-11T02:16:41.700 に答える