6

OpenMP と SIMD を使用して配列を削減したいと考えています。OpenMP の削減は次と同等であると読みました。

inline float sum_scalar_openmp2(const float a[], const size_t N) {
    float sum = 0.0f;
    #pragma omp parallel
    {
        float sum_private = 0.0f;
        #pragma omp parallel for nowait
        for(int i=0; i<N; i++) {
            sum_private += a[i];
        }
        #pragma omp atomic
        sum += sum_private;
    }
    return sum;
}

このアイデアは次のリンクから得ました: http://bisqwit.iki.fi/story/howto/openmp/#ReductionClause しかし、atomic は複雑な演算子もサポートしていません。私がしたことは、アトミックをクリティカルに置き換え、次のように OpenMP と SSE で削減を実装することでした。

#define ROUND_DOWN(x, s) ((x) & ~((s)-1))
inline float sum_vector4_openmp(const float a[], const size_t N) {
    __m128 sum4 = _mm_set1_ps(0.0f);
    #pragma omp parallel 
    {
        __m128 sum4_private = _mm_set1_ps(0.0f);
        #pragma omp for nowait
        for(int i=0; i < ROUND_DOWN(N, 4); i+=4) {
            __m128 a4 = _mm_load_ps(a + i);
            sum4_private = _mm_add_ps(a4, sum4_private);
        }
        #pragma omp critical
        sum4 = _mm_add_ps(sum4_private, sum4);
    }
    __m128 t1 = _mm_hadd_ps(sum4,sum4);
    __m128 t2 = _mm_hadd_ps(t1,t1);
    float sum = _mm_cvtss_f32(t2);  
    for(int i = ROUND_DOWN(N, 4); i < N; i++) {
        sum += a[i];
    }
    return sum;
} 

ただし、この機能は期待どおりには機能しません。Visual Studio 2012 Express を使用しています。SSE のロード/追加を数回アンロールすることでパフォーマンスを少し改善できることはわかっていますが、それでも期待したほどではありません。

スレッドの数に等しい配列のスライスを実行することで、パフォーマンスが大幅に向上します。

inline float sum_slice(const float a[], const size_t N) {
    int nthreads = 4;
    const int offset = ROUND_DOWN(N/nthreads, nthreads);
    float suma[8] = {0};
    #pragma omp parallel for num_threads(nthreads) 
    for(int i=0; i<nthreads; i++) {
        suma[i] = sum_vector4(&a[i*offset], offset);
    }
    float sum = 0.0f;
    for(int i=0; i<nthreads; i++) {
        sum += suma[i]; 
    }
    for(int i=nthreads*offset; i < N; i++) {
        sum += a[i];
    }
    return sum;    
}

inline float sum_vector4(const float a[], const size_t N) {
    __m128 sum4 = _mm_set1_ps(0.0f);
    int i = 0;
    for(; i < ROUND_DOWN(N, 4); i+=4) {
        __m128 a4 = _mm_load_ps(a + i);
        sum4 = _mm_add_ps(sum4, a4);
    }
    __m128 t1 = _mm_hadd_ps(sum4,sum4);
    __m128 t2 = _mm_hadd_ps(t1,t1);
    float sum = _mm_cvtss_f32(t2);
    for(; i < N; i++) {
        sum += a[i];
    }
    return sum;

}

OpenMP でより複雑な演算子を使用してリダクションを行うより良い方法があるかどうか誰かが知っていますか?

4

1 に答える 1

1

あなたの質問に対する答えは「いいえ」だと思います。OpenMPでより複雑な演算子を使用して削減を行うためのより良い方法はないと思います。

アレイが16ビットアラインされていると仮定すると、openmpスレッドの数は4であり、OpenMP+SIMDによるパフォーマンスの向上は12倍から16倍になると予想されます。現実的には、十分なパフォーマンスの向上が得られない可能性があります。

  1. openmpスレッドの作成にはオーバーヘッドがあります。
  2. コードは、1回のロード操作に対して1回の加算操作を実行しています。したがって、CPUは十分な計算を行っていません。したがって、CPUはほとんどの時間をデータのロードに費やしているように見えます。これは、メモリ帯域幅の制限のようなものです。
于 2013-03-16T04:51:20.757 に答える