12

OpenMP を使用してヒストグラムを並行して埋めたいと思います。C/C++ で OpenMP を使用してこれを行う 2 つの異なる方法を考え出しました。

最初の方法は、スレッドごとproccess_data_v1にプライベート ヒストグラム変数を作成し、それらを並列に入力してから、セクション内の共有ヒストグラムにプライベート ヒストグラムを合計します。hist_privatehistcritical

2 番目の方法proccess_data_v2は、スレッド数に等しい配列サイズのヒストグラムの共有配列を作成し、この配列を並行して埋めてから、共有ヒストグラムhistを並行して合計します。

2 番目の方法は、クリティカル セクションを回避し、ヒストグラムを並行して合計するため、優れているように思えます。ただし、スレッド数を把握して を呼び出す必要がありomp_get_thread_num()ます。私は通常、これを回避しようとします。スレッド番号を参照せずに、スレッド数と同じサイズの共有配列を使用せずに、2 番目の方法を実行するより良い方法はありますか?

void proccess_data_v1(float *data, int *hist, const int n, const int nbins, float max) {
    #pragma omp parallel 
    {
        int *hist_private = new int[nbins];
        for(int i=0; i<nbins; i++) hist_private[i] = 0;
        #pragma omp for nowait
        for(int i=0; i<n; i++) {
            float x = reconstruct_data(data[i]);
            fill_hist(hist_private, nbins, max, x);
        }
        #pragma omp critical 
        {
            for(int i=0; i<nbins; i++) {
                hist[i] += hist_private[i];
            }
        }
        delete[] hist_private;
    }
}

void proccess_data_v2(float *data, int *hist, const int n, const int nbins, float max) {
    const int nthreads = 8;
    omp_set_num_threads(nthreads);
    int *hista = new int[nbins*nthreads];

    #pragma omp parallel 
    {
        const int ithread = omp_get_thread_num();
        for(int i=0; i<nbins; i++) hista[nbins*ithread+i] = 0;
        #pragma omp for
        for(int i=0; i<n; i++) {
            float x = reconstruct_data(data[i]);
            fill_hist(&hista[nbins*ithread], nbins, max, x);
        }

        #pragma omp for
        for(int i=0; i<nbins; i++) {
            for(int t=0; t<nthreads; t++) {
                hist[i] += hista[nbins*t + i];
            }
        }

    }
    delete[] hista;
}

編集: @HristoIliev の提案に基づいて、私は改善されたメソッドを作成しましたprocess_data_v3

#define ROUND_DOWN(x, s) ((x) & ~((s)-1))
void proccess_data_v2(float *data, int *hist, const int n, const int nbins, float max) {
    int* hista;
    #pragma omp parallel 
    {
        const int nthreads = omp_get_num_threads();
        const int ithread = omp_get_thread_num();

        int lda = ROUND_DOWN(nbins+1023, 1024);  //1024 ints = 4096 bytes -> round to a multiple of page size
        #pragma omp single
        hista = (int*)_mm_malloc(lda*sizeof(int)*nthreads, 4096);  //align memory to page size

        for(int i=0; i<nbins; i++) hista[lda*ithread+i] = 0;
        #pragma omp for
        for(int i=0; i<n; i++) {
            float x = reconstruct_data(data[i]);
            fill_hist(&hista[lda*ithread], nbins, max, x);
        }

        #pragma omp for
        for(int i=0; i<nbins; i++) {
            for(int t=0; t<nthreads; t++) {
                hist[i] += hista[lda*t + i];
            }
        }

    }
    _mm_free(hista);
}
4

1 に答える 1

5

使用されている実際のスレッド数についてクエリを実行できる並列領域内に大きな配列を割り当てることができます。

int *hista;
#pragma omp parallel 
{
    const int nthreads = omp_get_num_threads();
    const int ithread = omp_get_thread_num();

    #pragma omp single
    hista = new int[nbins*nthreads];

    ...
}
delete[] hista;

パフォーマンスを向上させるために、各スレッドのチャンクのサイズをhistaシステムのメモリ ページ サイズの倍数に丸めることをお勧めします。これにより、異なる部分ヒストグラム間に穴が残る可能性があります。このようにして、NUMA システムでの誤った共有とリモート メモリ アクセスの両方を防ぐことができます (ただし、最終的な削減フェーズではできません)。

于 2013-05-28T12:05:37.367 に答える