4

私は、MPICH と比較して OpenMP のポイントを証明しようとしていました。OpenMP で高いパフォーマンスを実現することがいかに簡単かを示すために、次の例を作成しました。

Gauss-Seidel 反復は、各スイープですべての操作を任意の順序で実行でき、各タスク間に依存関係がないように、2 つの個別の実行に分割されます。したがって、理論的には、各プロセッサは、別のプロセスが何らかの同期を実行するのを待つ必要はありません。

私が直面している問題は、問題のサイズに関係なく、2 プロセッサの速度向上は弱く、2 プロセッサを超えるとさらに遅くなる可能性があることです。他の多くの線形並列ルーチンは非常に優れたスケーリングを得ることができますが、これは注意が必要です。

私が恐れているのは、配列に対して実行する操作がスレッドセーフであることをコンパイラーに「説明」できないため、実際には効果的ではないということです。

以下の例を参照してください。

OpenMP でこれをより効果的にする方法についての手がかりはありますか?

void redBlackSmooth(std::vector<double> const & b,
                    std::vector<double> & x,
                    double h)
{
    // Setup relevant constants.
    double const invh2 = 1.0/(h*h);
    double const h2 = (h*h);
    int const N = static_cast<int>(x.size());
    double sigma = 0;

    // Setup some boundary conditions.
    x[0] = 0.0;
    x[N-1] = 0.0;

    // Red sweep.
    #pragma omp parallel for shared(b, x) private(sigma)
    for (int i = 1; i < N-1; i+=2)
    {
        sigma = -invh2*(x[i-1] + x[i+1]);
        x[i] = (h2/2.0)*(b[i] - sigma);
    }

    // Black sweep.
    #pragma omp parallel for shared(b, x) private(sigma)
    for (int i = 2; i < N-1; i+=2)
    {
        sigma = -invh2*(x[i-1] + x[i+1]);
        x[i] = (h2/2.0)*(b[i] - sigma);
    }
}

追加:生のポインター実装も試してみましたが、STL コンテナーを使用するのと同じ動作をするため、STL からの疑似クリティカルな動作である可能性を除外できます。

4

2 に答える 2

0

主な問題は、使用している配列構造のタイプに関するものだと思います。結果をベクトルと配列で比較してみましょう。(配列 = new 演算子を使用した c 配列)。

ベクトルと配列のサイズは N = 10000000 です。実行時間を 0.1 秒以上に維持するために、平滑化関数を強制的に繰り返します。

Vector Time:    0.121007        Repeat: 1       MLUPS:  82.6399

Array Time:     0.164009        Repeat: 2       MLUPS:  121.945

MLUPS = ((N-2)*repeat/runtime)/1000000 (Million Lattice Points Update per second)

グリッド計算に関しては、MFLOPS は誤解を招く可能性があります。基本的な方程式を少し変えるだけで、同じ実行時間で高いパフォーマンスを考慮することができます。

変更されたコード:

double my_redBlackSmooth(double *b, double* x, double h, int N)
{
    // Setup relevant constants.
    double const invh2 = 1.0/(h*h);
    double const h2 = (h*h);
    double sigma = 0;

    // Setup some boundary conditions.
    x[0] = 0.0;
    x[N-1] = 0.0;

    double runtime(0.0), wcs, wce;
    int repeat = 1;
    timing(&wcs);
    for(; runtime < 0.1; repeat*=2)
    {
        for(int r = 0; r < repeat; ++r)
        {
            // Red sweep.
            #pragma omp parallel for shared(b, x) private(sigma)
            for (int i = 1; i < N-1; i+=2)
            {
                sigma = -invh2*(x[i-1] + x[i+1]);
                x[i] = (h2*0.5)*(b[i] - sigma);
            }
            // Black sweep.
            #pragma omp parallel for shared(b, x) private(sigma)
            for (int i = 2; i < N-1; i+=2)
            {
                sigma = -invh2*(x[i-1] + x[i+1]);
                x[i] = (h2*0.5)*(b[i] - sigma);
            }
            //  cout << "In Array:      " << r << endl;
        }
        if(x[0] != 0) dummy(x[0]);
        timing(&wce);
        runtime = (wce-wcs);
    }
    //  cout << "Before division:   " << repeat << endl;
    repeat /= 2;
    cout << "Array Time:\t" << runtime << "\t" << "Repeat:\t" << repeat
         << "\tMLUPS:\t" << ((N-2)*repeat/runtime)/1000000.0 << endl;

    return runtime;
}

配列型以外のコードは何も変更していません。キャッシュ アクセスとブロックを改善するには、データの配置 (_mm_malloc) を調べる必要があります。

于 2013-04-04T08:23:44.940 に答える