簡體   English   中英

CUDA指針算術是否導致未分批的內存訪問?

[英]CUDA pointer arithmetic causes uncoalesced memory access?

我正在使用必須在指針到指針上運行的CUDA內核。 內核基本上執行大量非常小的縮減,最好以串行方式完成,因為縮減的大小為Nptrs = 3-4。 這是內核的兩種實現:

__global__
void kernel_RaiseIndexSLOW(double*__restrict__*__restrict__ A0,
        const double*__restrict__*__restrict__ B0,
        const double*__restrict__*__restrict__ C0,
        const int Nptrs, const int Nx){
      const int i = blockIdx.y;
      const int j = blockIdx.z;
      const int idx = blockIdx.x*blockDim.x + threadIdx.x;
      if(i<Nptrs) {
         if(j<Nptrs) {
           for (int x = idx; x < Nx; x += blockDim.x*gridDim.x){
              A0gpu[i+3*j][x] = B0gpu[i][x]*C0gpu[3*j][x]
                       +B0gpu[i+3][x]*C0gpu[1+3*j][x]
                       +B0gpu[i+6][x]*C0gpu[2+3*j][x];               
           }
         }
       }
 }

__global__
void kernel_RaiseIndexsepderef(double*__restrict__*__restrict__  A0gpu, 
               const double*__restrict__*__restrict__ B0gpu,
               const double*__restrict__*__restrict__ C0gpu,
               const int Nptrs, const int Nx){
const int i = blockIdx.y;
const int j = blockIdx.z;
const int idx = blockIdx.x*blockDim.x + threadIdx.x;
if(i<Nptrs) {
  if(j<Nptrs){
    double*__restrict__ A0ptr = A0gpu[i+3*j];
    const double*__restrict__ B0ptr0 = B0gpu[i];
    const double*__restrict__ C0ptr0 = C0gpu[3*j];
    const double*__restrict__ B0ptr1 = B0ptr0+3;
    const double*__restrict__ B0ptr2 = B0ptr0+6;
    const double*__restrict__ C0ptr1 = C0ptr0+1;
    const double*__restrict__ C0ptr2 = C0ptr0+2;

    for (int x = idx; x < Nx; x +=blockDim.x *gridDim.x){
      double d2 = C0ptr0[x];
      double d4 = C0ptr1[x]; //FLAGGED
      double d6 = C0ptr2[x]; //FLAGGED
      double d1 = B0ptr0[x];
      double d3 = B0ptr1[x]; //FLAGGED
      double d5 = B0ptr2[x]; //FLAGGED
      A0ptr[x] = d1*d2 + d3*d4 + d5*d6;

    }
   }                                                                        
  }
 }

如名稱所示,內核“ sepderef”的性能比同類產品快約40%,一旦計算出啟動開銷,則在Nptrs = 3的情況下,在M2090上,ECC((〜160GBps)在Nptrs = 3時,有效帶寬為約85GBps。最佳)。

通過nvvp運行這些命令表明內核受帶寬限制。 但是,奇怪的是,探查器將我標記為// FLAGGED的行突出顯示為次優內存訪問區域。 我不明白為什么會這樣,因為這里的訪問對我來說是合並的。 為什么不會呢?

編輯:我忘了指出這一點,但是請注意// FLAGGED區域正在訪問我在上面進行算術運算的指針,而其他區域則使用方括號運算符進行訪問。

要了解這種行為,需要知道到目前為止所有CUDA GPU都按順序執行指令。 在發出從內存中加載操作數的指令后,其他獨立的指令仍然繼續執行。 但是,一旦遇到一條依賴於內存中操作數的指令,該指令流上的所有進一步操作都將停止,直到該操作數變為可用為止。

在您的“ sepderef”示例中,您是在從內存中加載所有操作數之前對其求和,這意味着每個循環迭代可能僅引起一次全局內存延遲(每個循環迭代有六個加載,但它們都可以重疊。)循環的第一個加法將停頓,直到它的操作數可用為止。停頓之后,所有其他加法將使它們的操作數容易或很快可用)。

在“ SLOW”示例中,來自內存的加載和添加操作混合在一起,因此每次循環操作都會導致全局內存延遲多次。

您可能想知道為什么編譯器在計算之前不會自動對加載指令進行重新排序。 CUDA編譯器過去非常積極地執行此操作,在操作數等待使用之前,在這些寄存器上增加了額外的寄存器。 但是,CUDA 8.0在這方面似乎沒有那么積極,而是更加遵循源代碼中的指令順序。 這為程序員提供了更好的機會, 即在編譯器的指令調度不理想的情況下 ,以最佳的性能方式構造代碼。 同時,即使以前的編譯器版本正確,顯式地調度指令也給程序員增加了負擔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM