1

これは私の最初の質問です。私は、openMP で C の 2d haar 変換関数を並列化しようとしています。ここで取得し、それに応じて変更しました。このプログラムは白黒画像を取得し、それを行列に入れ、haar ウェーブレット変換の 1 つのレベルを計算します。最後に、値を正規化し、変換されたイメージをディスクに書き込みます。

これはHDTの結果のイメージ1 レベルです。

私の問題は、並列化されたバージョンがシリアル バージョンよりもかなり遅く実行されることです。今のところ、並列化したい主要部分のスニペットをここに添付します (後で、周囲のすべてのコードを配置できます)。

void haar_2d ( int m, int n, double u[] )
// m & n are the dimentions (every image is a perfect square)
//u is the input array in **(non column-major!)** row-major order</del>
int i;
int j;
int k;
double s;
double *v;

int tid, nthreads, chunk;

s = sqrt ( 2.0 );

v = ( double * ) malloc ( m * n * sizeof ( double ) );

for ( j = 0; j < n; j++ )
{
    for ( i = 0; i < m; i++ )
    {
        v[i+j*m] = u[i+j*m];
    }
}
/*
Determine K, the largest power of 2 such that K <= M.
*/
k = 1;
while ( k * 2 <= m )
{
    k = k * 2;
}

/*   Transform all columns.  */

while ( n/2 < k ) // just 1 level of transformation
{
    k = k / 2;

    clock_t begin = clock();

    #pragma omp parallel shared(s,v,u,n,m,nthreads,chunk) private(i,j,tid)
    {
        tid = omp_get_thread_num();
        printf("Thread %d starting...\n",tid);

        #pragma omp for schedule (dynamic)
        for ( j = 0; j < n; j++ )
        {
            for ( i = 0; i < k; i++ )
            {               
                v[i  +j*m] = ( u[2*i+j*m] + u[2*i+1+j*m] ) / s;
                v[k+i+j*m] = ( u[2*i+j*m] - u[2*i+1+j*m] ) / s;
            }
        }

    #pragma omp for schedule (dynamic)
    for ( j = 0; j < n; j++ )
    {
        for ( i = 0; i < 2 * k; i++ )
        {
            u[i+j*m] = v[i+j*m];
        }
    }
}//end parallel

clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
printf ( "Time for COLUMNS: %f ms\n", time_spent * 1000);

}//end while

// [...]code for rows
free ( v );

return;}

タイミングは多かれ少なかれ次のとおりです。

Time for COLUMNS: 160.519000 ms // parallel
Time for COLUMNS: 62.842000 ms // serial

プラグマをさまざまな方法で再配置しようとしました。たとえば、静的スケジュール、セクション、タスクなどを使用して、変数のデータ スコープを再配置し、並列領域内で動的に割り当てます。2 レベルの for を並列化するのは簡単だと思っていましたが、苦労して 2 日が経過しました。あなたの助けを求めて、ここで関連するすべての質問をすでにチェックアウトしましたが、まだ先に進むことができないか、少なくとも理由を理解することができません. 前もって感謝します。(CPU Intel Core i3-4005U CPU @ 1.70GHz × 4 スレッド、2 コア)

アップデート:

1)m&nについては、いつか長方形の画像も実装することになっているので、そのままにしておきました。

2) u は実際には行ごとに線形化された行列を含む通常の配列であることがわかりました (私は PGM 画像を使用しています)。

3) memcpy の方が優れたオプションであるため、現在は memcpy を使用しています。

主なトピックについてはどうですか。チャンクごとにタスクを生成することにより、ジョブをn個に分割しようとしましたが、結果はシリアルコードより少し高速です。これで、入力行列 u が適切な行優先順序になっていることがわかりました。2 つの fors はそれに応じて進行しているように見えますが、タイミングについてはわかりません: omp_get_wtime() と clock() の両方を使用する方法がわかりませんスピードアップを測定します。16x16 から 4096x4096 までのさまざまな画像サイズでテストを行いましたが、並列バージョンは clock() で遅くなり、omp_get_wtime() と gettimeofday() で速くなるようです。OpenMPで正しく処理する方法、または少なくともスピードアップを正しく測定する方法についていくつか提案がありますか?

while ( n/2 < k )
{
    k = k / 2;
    double start_time = omp_get_wtime();
    // clock_t begin = clock();
    #pragma omp parallel shared(s,v,u,n,m,nthreads,chunk) private(i,j,tid) firstprivate(k)
    {
        nthreads = omp_get_num_threads();

         #pragma omp single
         {
          printf("Number of threads = %d\n", nthreads);

          int chunk = n/nthreads;
          printf("Chunks size = %d\n", chunk);
          printf("Thread %d is starting the tasks.\n", omp_get_thread_num());

          int h;

          for(h=0;h<n;h = h + chunk){
          printf("FOR CYCLE i=%d\n", h);

            #pragma omp task shared(s,v,u,n,m,nthreads,chunk) private(i,j,tid) firstprivate(h,k)
            {
                tid = omp_get_thread_num();
                 printf("Thread %d starts at %d position\n", tid , h);

                for ( j = h; j < h + chunk; j++ )
                {
                    for ( i = 0; i < k; i++ )
                    {
                        v[i  +j*m] = ( u[2*i+j*m] + u[2*i+1+j*m] ) / s;
                        v[k+i+j*m] = ( u[2*i+j*m] - u[2*i+1+j*m] ) / s;
                    }
                }
            }// end task
        }//end launching for
        #pragma omp taskwait
        }//end single
        }//end parallel region

        // clock_t end = clock();
        // double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
        // printf ( "COLUMNS: %f ms\n", time_spent * 1000);

        double time = omp_get_wtime() - start_time;
        printf ( "COLUMNS: %f ms\n", time*1000);

    for ( j = 0; j < n; j++ )
    {
        for ( i = 0; i < 2 * k; i++ )
        {
            u[i+j*m] = v[i+j*m];
        }
    }
 }//end while
4

2 に答える 2

0

あなたのコードについて深く懸念する質問がいくつかあります。

  1. m & n は寸法です (すべての画像は完全な正方形です)

    では、なぜ 2 つのサイズ パラメータがあるのでしょうか。

  2. u は列優先順の入力配列です

    これは信じられないほど悪い考えです。C はメモリに行優先の順序付けを使用するため、列優先のインデックス付けはストライド メモリ アクセスにつながります。これは、パフォーマンスにとって非常に悪いことです。可能であれば、これを修正する必要があります。

  3. uとは両方ともv線形化された行列であるため、これは

    for (int j = 0; j < n; j++) {
        for (int i = 0; i < m; i++) {
            v[i + j * m] = u[i + j * m];
        }
    }
    

    への呼び出しに置き換えることができますmemcpy

    memcpy(v, u, m * n * sizeof(double));
    

あなたの問題に。OpenMP を使用したバージョンが遅い理由は、すべてのスレッドが同じことを行っているためです。これは役に立たず、偽の共有などの悪いことにつながります。tidスレッド間でデータを分割するには、各スレッドの ID (コード内) を使用する必要があります。偽の共有は悪いことに注意してください。

于 2016-07-12T03:05:56.330 に答える
0

問題は、Z boson のおかげで、omp_get_wtime() の代わりに clock() を使用していたことです。

于 2016-11-27T15:43:57.357 に答える