2

Cyclic Reductionのメソッドに基づいて、三重対角システム ソルバーを実装しようとしていGTS450ます。

巡回還元はこの論文で説明されています

Y. Zhang、J. Cohen、JD Owens、「GPU 上の高速三重対角ソルバー」

ただし、何をしても、私の CUDA コードは順次対応するコードよりもはるかに低速です。合計512 x 512ポイントの結果は ですが7ms、私の i7 3.4GHz では です5ms。GPU が加速していません。

どちらが問題になる可能性がありますか?

#include "cutrid.cuh"
 __global__ void cutrid_RC_1b(double *a,double *b,double *c,double *d,double *x)
{
 int idx_global=blockIdx.x*blockDim.x+threadIdx.x;
 int idx=threadIdx.x;

 __shared__ double asub[512];
 __shared__ double bsub[512];
 __shared__ double csub[512];
 __shared__ double dsub[512];

 double at=0;
 double bt=0;
 double ct=0;
 double dt=0;

 asub[idx]=a[idx_global];
 bsub[idx]=b[idx_global];
 csub[idx]=c[idx_global];
 dsub[idx]=d[idx_global];


 for(int stride=1;stride<N;stride*=2)
  {
    int margin_left,margin_right;
    margin_left=idx-stride;
    margin_right=idx+stride;


    at=(margin_left>=0)?(-csub[idx-stride]*asub[idx]/bsub[idx-stride]):0.f; 

    bt=bsub[idx]+((margin_left>=0)?(-csub[idx-stride]*asub[idx]/bsub[idx-stride]):0.f)
    -((margin_right<512)?asub[idx+stride]*csub[idx]/bsub[idx+stride]:0.f); 

    ct=(margin_right<512)?(-csub[idx+stride]*asub[idx]/bsub[idx+stride]):0.f; 

    dt=dsub[idx]+((margin_left>=0)?(-dsub[idx-stride]*asub[idx]/bsub[idx-stride]):0.f)
    -((margin_right<512)?dsub[idx+stride]*csub[idx]/bsub[idx+stride]:0.f); 

    __syncthreads();
    asub[idx]=at;
    bsub[idx]=bt;
    csub[idx]=ct;
    dsub[idx]=dt;
    __syncthreads();
  }


x[idx_global]=dsub[idx]/bsub[idx];

}/*}}}*/

このカーネルをで起動し、デバイス占有率cutrid_RC_1b<<<512,512>>>(d_a,d_b,d_c,d_d,d_x)に達しました。100%この結果は何日も私を困惑させました。

私のコードの改良版があります:

    #include "cutrid.cuh"
    __global__ void cutrid_RC_1b(float *a,float *b,float *c,float *d,float *x)
    {/*{{{*/
     int idx_global=blockIdx.x*blockDim.x+threadIdx.x;
     int idx=threadIdx.x;

     __shared__ float asub[512];
     __shared__ float bsub[512];
     __shared__ float csub[512];
     __shared__ float dsub[512];

    asub[idx]=a[idx_global];
    bsub[idx]=b[idx_global];
    csub[idx]=c[idx_global];
    dsub[idx]=d[idx_global];
 __syncthreads();
   //Reduction  
    for(int stride=1;stride<512;stride*=2)
    {
        int margin_left=(idx-stride);
        int margin_right=(idx+stride);
        if(margin_left<0) margin_left=0;
        if(margin_right>=512) margin_right=511;
        float tmp1 = asub[idx] / bsub[margin_left];
        float tmp2 = csub[idx] / bsub[margin_right];
        float tmp3 = dsub[margin_right];
        float tmp4 = dsub[margin_left];
        __syncthreads();

        dsub[idx] = dsub[idx] - tmp4*tmp1-tmp3*tmp2;
        bsub[idx] = bsub[idx]-csub[margin_left]*tmp1-asub[margin_right]*tmp2;

        tmp3 = -csub[margin_right]; 
        tmp4 = -asub[margin_left];

        __syncthreads();
        asub[idx] = tmp3*tmp1;
        csub[idx] = tmp4*tmp2;
        __syncthreads();
     }

        x[idx_global]=dsub[idx]/bsub[idx];

    }/*}}}*/

forシステムでは速度が改善さ0.73msれますが、前述の論文のコードは.Quadro k4000512 x 5120.5msGTX280

4

3 に答える 3

6

古典的な解法、つまりガウス消去法は本質的に逐次的であるため、三重対角系の連立方程式を解くことは困難な並列問題です。

巡回削減は、次の 2 つのフェーズで構成されます。

  1. 前方削減。元のシステムは、奇数のインデックスを持つものと偶数のインデックスを持つ未知の 2 つのセットの 2 つの独立した三重対角システムに分割されます。このようなシステムは独立して解くことができ、このステップは分割と帝国のスキームの最初のものと見なすことができます。22 つの小さなシステムは、同じ方法で 2 つのサブシステムに再び分割され、方程式のみのシステムに到達するまでプロセスが繰り返されます。
  2. 後方置換。連立方程式2が最初に解かれます。次に、サブシステムを異なるコアで独立して解決することにより、分割と支配の構造を上っていきます。

あなたのコードが一貫した結果を返すかどうかはわかりません (間違っていたら訂正してください)。N定義されていないようです。また、あなたは にアクセスcsub[idx-stride]していますが、いつidx==0stride>1. さらに、本質的に境界チェックのために、いくつかの条件ステートメントを使用しています。最後に、あなたのコードには、CUDA SDK リダクション サンプルで使用されているものと概念的にかなり似ている、前述の分割と帝国スキームを処理できる適切なスレッド構造がありません。

上記の私のコメントの 1 つで述べたように、tridiagonalsolversで、三重対角方程式系を解くための巡回縮約スキームの実装を見つけることができることを思い出しました。関連する Google ページをブラウズすると、上記の論文の最初の著者 (Yao Zhang) によってコードが管理されているように思えます。コードをコピーして以下に貼り付けます。境界チェックは 1 回だけ ( if (iRight >= systemSize) iRight = systemSize - 1;) 実行されるため、関係する条件ステートメントの数が制限されることに注意してください。分割と帝国のスキームを処理できるスレッド構造にも注意してください。

Zhang、Cohen、Owens によるコード

__global__ void crKernel(T *d_a, T *d_b, T *d_c, T *d_d, T *d_x)
{
   int thid = threadIdx.x;
   int blid = blockIdx.x;

   int stride = 1;

   int numThreads = blockDim.x;
   const unsigned int systemSize = blockDim.x * 2;

   int iteration = (int)log2(T(systemSize/2));
   #ifdef GPU_PRINTF 
    if (thid == 0 && blid == 0) printf("iteration = %d\n", iteration);
   #endif

   __syncthreads();

   extern __shared__ char shared[];

   T* a = (T*)shared;
   T* b = (T*)&a[systemSize];
   T* c = (T*)&b[systemSize];
   T* d = (T*)&c[systemSize];
   T* x = (T*)&d[systemSize];

   a[thid] = d_a[thid + blid * systemSize];
   a[thid + blockDim.x] = d_a[thid + blockDim.x + blid * systemSize];

   b[thid] = d_b[thid + blid * systemSize];
   b[thid + blockDim.x] = d_b[thid + blockDim.x + blid * systemSize];

   c[thid] = d_c[thid + blid * systemSize];
   c[thid + blockDim.x] = d_c[thid + blockDim.x + blid * systemSize];

   d[thid] = d_d[thid + blid * systemSize];
   d[thid + blockDim.x] = d_d[thid + blockDim.x + blid * systemSize];

   __syncthreads();

   //forward elimination
   for (int j = 0; j <iteration; j++)
   {
       __syncthreads();
       stride *= 2;
       int delta = stride/2;

    if (threadIdx.x < numThreads)
    {
        int i = stride * threadIdx.x + stride - 1;
        int iLeft = i - delta;
        int iRight = i + delta;
        if (iRight >= systemSize) iRight = systemSize - 1;
        T tmp1 = a[i] / b[iLeft];
        T tmp2 = c[i] / b[iRight];
        b[i] = b[i] - c[iLeft] * tmp1 - a[iRight] * tmp2;
        d[i] = d[i] - d[iLeft] * tmp1 - d[iRight] * tmp2;
        a[i] = -a[iLeft] * tmp1;
        c[i] = -c[iRight] * tmp2;
    }
       numThreads /= 2;
   }

   if (thid < 2)
   {
     int addr1 = stride - 1;
     int addr2 = 2 * stride - 1;
     T tmp3 = b[addr2]*b[addr1]-c[addr1]*a[addr2];
     x[addr1] = (b[addr2]*d[addr1]-c[addr1]*d[addr2])/tmp3;
     x[addr2] = (d[addr2]*b[addr1]-d[addr1]*a[addr2])/tmp3;
   }

   // backward substitution
   numThreads = 2;
   for (int j = 0; j <iteration; j++)
   {
       int delta = stride/2;
       __syncthreads();
       if (thid < numThreads)
       {
           int i = stride * thid + stride/2 - 1;
           if(i == delta - 1)
                 x[i] = (d[i] - c[i]*x[i+delta])/b[i];
           else
                 x[i] = (d[i] - a[i]*x[i-delta] - c[i]*x[i+delta])/b[i];
        }
        stride /= 2;
        numThreads *= 2;
     }

   __syncthreads();

   d_x[thid + blid * systemSize] = x[thid];
   d_x[thid + blockDim.x + blid * systemSize] = x[thid + blockDim.x];

}

于 2013-10-23T21:17:59.083 に答える
0

私が見るもの:

  1. 1番目__syncthreads()は冗長なようです。

  2. コードのように、一連の反復操作があり(-csub[idx-stride]*asub[idx]/bsub[idx-stride])ます。GPU に毎回これらのセットを計算させる代わりに、中間変数を使用して結果を保持し、それらを再利用します。

  3. NVIDIA プロファイラーを使用して、問題がどこにあるかを確認します。

于 2013-10-23T17:15:17.927 に答える