CUDA Shared Memory : transpose

CUDA Shared Memory : transpose







延�m上一篇�v thread block,�@一篇能然是�^�m�v transpose �@���例程式;不�^�@一篇,�t是�⒔裹c放在 shared memory 的使用,也就是 transpose �@��最佳化�^的 kernel 函式。



首先先�砜匆幌� shared memory 的特色。在《nVidia CUDA �介》 中,有大概提�^各�N不同���的差��了~而 shared memory 基本上是在晶片上的,只能由同一�� thread block �e的thread �磉M行存取;而他的���c在於他的速度比 global memory 或 local memory都�淼目欤���H上,在最好的情�r下(避免掉 bank conflict),���是可以和 register 一�涌斓摹�(�P於 sharedmemory 的效率���},���热菘�⒖肌�CUDA Programming Guide 1.1》的 5.1.2.4)而在《CUDA Programming Guide 1.1》的《5.1.2 Memory Bandwidth》中,也提供了��实� shared memory 使用的 pattern:


  • �①Y料由 device memory �D存到 shared memory  

  • � block 中的 thread 同步,如此才能�_保每�� thread 能安全的�x到其他 thread ��到 shared memory 的�Y料  

  • �理 shared memory 中的�Y料  

  • 如果需要,再同步一次;� shared memory �e的�Y料完成更新  

  • �⒔Y果��回到 device memory


而在 transpose �@��例子�e,他透�^ shared memory �碜鲎罴鸦�,其��主要���不是他的速度,而是要透�^使用 sharedmemory �肀苊��於�鬟M的 global memory 有 non-coalesced 的存取,而同�r也避免使用 sharedmemory �r有 bank conflict 而拖慢速度。
而其最佳化後的程式�a如下:

  1. // This kernel is optimized to ensure all global reads and writes are coalesced,
  2. // and to avoid bank conflicts in shared memory.  This kernel is up to 11x faster
  3. // than the naive kernel below.  Note that the shared memory array is sized to
  4. // (BLOCK_DIM+1)*BLOCK_DIM.  This pads each row of the 2D block in shared memory
  5. // so that bank conflicts do not occur when threads address the array column-wise.
  6. __global__ void transpose(float *odata, float *idata, int width, int height)
  7. {
  8.     __shared__ float block[BLOCK_DIM][BLOCK_DIM+1];
  9.     // read the matrix tile into shared memory
  10.     unsigned int xIndex = blockIdx.x * BLOCK_DIM + threadIdx.x;
  11.     unsigned int yIndex = blockIdx.y * BLOCK_DIM + threadIdx.y;
  12.     if((xIndex < width) && (yIndex < height))
  13.     {
  14.         unsigned int index_in = yIndex * width + xIndex;
  15.         block[threadIdx.y][threadIdx.x] = idata[index_in];
  16.     }

  17.     __syncthreads();

  18.     // write the transposed matrix tile to global memory
  19.     xIndex = blockIdx.y * BLOCK_DIM + threadIdx.x;
  20.     yIndex = blockIdx.x * BLOCK_DIM + threadIdx.y;
  21.     if((xIndex < height) && (yIndex < width))
  22.     {
  23.         unsigned int index_out = yIndex * height + xIndex;
  24.         odata[index_out] = block[threadIdx.x][threadIdx.y];
  25.     }
  26. }
复制代码

在上面的程式中可以看出�恚�第一行的

  1. __shared__ float block[BLOCK_DIM][BLOCK_DIM+1];
复制代码

就是透�^ __shared__ �@����档� qualifier �硇�告出一��大小是 BLOCK_DIM *(BLOCK_DIM+1) 的二�S�列。而�槭颤N�@�有�告呢?主要就是因�� shared memory 是在同一�� block �e的thread 共用的,而在�@���例�e,每�� block �e都有 BLOCK_DIM * BLOCK_DIM �� thread要�磉M行�算;因此,他宣告�@��二�S�列的目的,就是在 shared memory �e�a生一�K����w空�g( block),拿�泶娣琶恳�� thread 要�� global memory 中�x取的�Y料( idata)。但是�槭颤N���成 BLOCK_DIM * (BLOCK_DIM+1) 呢?在函式的�]解��f�@是�榱吮苊庠���列做 column-wise 存取�r�a生 bank conflict 而影�效率,�@�c Heresy 也��]研究完,所以就先跳�^了。



而接下�淼�幼鳎�就是把�Y料�� global memory �e�}�u到 shared memory 了~而�@�做的事,除了本�碓�做的�l件判�嗤猓�再�砭褪怯�算�Y料在 idata 中���的位置,�K把他�}�u到 block �@�� shared memory 的��笛e了。

  1. unsigned int index_in = yIndex * width + xIndex;
  2. block[threadIdx.y][threadIdx.x] = idata[index_in];
复制代码

而由於�@些步�E都是每�� thread 各自做,然後各自填到 block 中,所以在�Y束後,必�要呼叫 __syncthreads() �@��函式��� block 中的 thread 同步,�_定 block 中的所有 thread 都已�完成 block[threadIdx.y][threadIdx.x] 的��入�幼鳌T� __syncthreads() �绦型瓿舍幔��@�� thread block ���已��� idata�@ �� array 中取出 BLOCK_DIM * BLOCK_DIM 大小的�^域,放置到 shared memory 了~而由於每��thread block 都��做同�拥�幼鳎�所以就相��於整�� array 被切割成���大小是 BLOCK_DIM * BLOCK_DIM的部分�硖�理了。


而再接下�淼某淌剑�就是透�^已��� idata �}�u出�淼木植康木仃��Y料 block 中,把值再填入要�出的��� odata 中。 而由於他是直接把值��到�凫� global memory 的��担�所以其��就不需要呼叫 __syncthreads() ��� block 中的 thread 做同步了。


而到�@��橹梗�大概就是 CUDA 中 Shared memory 基本的使用方法了~其他的�|西,以後有空再�f吧~ ^^"

你可能感兴趣的:(职场,休闲)