0

私のコードには、ny*nx 行列内を移動するためのネストされたループを含むメソッドがいくつかあります。プロセスを並列化したかったので、すべてのメソッドで次のようなものを使用しました。

#pragma omp parallel for private(jj,x_e,x_w,y_n,y_s)
  for(ii=0;ii<ny;ii++) {
    for(jj=0;jj<nx;jj++) {
      /* determine indices of axis-direction neighbours
      ** respecting periodic boundary conditions (wrap around) */
      y_n = (ii + 1) % ny;
      x_e = (jj + 1) % nx;
      y_s = (ii == 0) ? (ii + ny - 1) : (ii - 1);
      x_w = (jj == 0) ? (jj + nx - 1) : (jj - 1);
      //propagate densities to neighbouring cells, following
      tmp[ii *nx + jj].speeds[0]  = cells[ii*nx + jj].speeds[0]; /* central cell, */
                                                                                     /* no movement   */
      tmp[ii *nx + x_e].s[1] = cells[ii*nx + jj].s[1]; /* east */
      tmp[y_n*nx + jj].s[2]  = cells[ii*nx + jj].s[2]; /* north */
      tmp[ii *nx + x_w].s[3] = cells[ii*nx + jj].s[3]; /* west */
      tmp[y_s*nx + jj].s[4]  = cells[ii*nx + jj].s[4]; /* south */
      tmp[y_n*nx + x_e].s[5] = cells[ii*nx + jj].s[5]; /* north-east */
      tmp[y_n*nx + x_w].s[6] = cells[ii*nx + jj].s[6]; /* north-west */
      tmp[y_s*nx + x_w].s[7] = cells[ii*nx + jj].s[7]; /* south-west */      
      tmp[y_s*nx + x_e].s[8] = cells[ii*nx + jj].s[8]; /* south-east */      
    }
  }

ただし、このコード (およびその他のコード) は非常に低速です。#pragma ステートメントを修正し、データ構造またはループを書き直して、キャッシュに適したものにし、誤った共有を回避する方法はありますか?

PS: コードは でコンパイルされて-O3いるため、マイナーな最適化を試みるたびに速度が向上しませんでした。

4

2 に答える 2

0

偽共有は、共有キャッシュ ラインが変更されている、つまり書き込まれているときに発生します。それを考えると、操作を逆にするだけで、コードのメモリ アクセス パターンの局所性が大幅に改善されcellsますtmp

tmp[ii*nx + jj].s[0] = cells[ii*nx + jj].s[0];
tmp[ii*nx + jj].s[1] = cells[...].s[1];
...
tmp[ii*nx + jj].s[8] = cells[...].s[8];

こうすることで、各スレッドのメモリ書き込みパターンを線形化し、キャッシュ フレンドリーにすると同時に、誤った共有を減らすことができます。

また、コードのパフォーマンスは主にメモリ帯域幅によって制限されるため、大きな配列の場合、より多くのメモリ帯域幅が提供されない限り、複数のスレッドを使用しても速度が向上しない可能性があることに注意してください。たとえば、各ソケットに独自のメモリ コントローラーと、各スレッドが異なるソケットのコアで実行されます。nxof 200 およびof 300を使用したテストにnyは、少なくとも 8,2 MiB のメモリが必要であり、ほとんどのデスクトップ プロセッサの最終レベルのキャッシュにはほとんど収まりませんが、ほとんどのサーバー クラスの CPU のキャッシュには収まります。より大きな 2000 x 3000 のケースは、確実にメモリに制約されます。

于 2013-10-20T10:27:10.480 に答える
0

スレッドを設定してそれらに作業を分散すると、いくらかのオーバーヘッドが発生します。作業サイズが小さく (200x300)、作業が単純 (一部のデータ コピーのみ) であるため、スレッド化のオーバーヘッドが実際の作業よりも大きくなる可能性があります。これが、パフォーマンスが向上しない理由の 1 つです。

もう 1 つの理由は、構造体の配列 (AoS) を使用しているため、コードのキャッシュ ローカリゼーションが不十分であることです。特にあなたがに書いているときtmp。キャッシュ ヒットのパフォーマンスを向上させるには、配列の構造 (SoA) の使用を検討できます。実際には、寸法を交換するだけで済みます。

cells[nx*ny][9]

cells[9][nx*ny]

memcpy()次に、コピーの一部を直接実行できることがわかります。

一方、あなたのコードに重大な偽共有の問題があるとは思いません。

于 2013-10-20T06:37:31.170 に答える