CUDA學習筆記(三)——CUDA內存


轉自:http://blog.sina.com.cn/s/blog_48b9e1f90100fm5f.html

結合lec07_intro_cuda.pptx學習

內存類型

       CGMA: Compute to Global Memory Access ratio

 

       Constant memory只允許device只讀,比global memory 能夠提供更快更多的並行數據訪問路徑給kernel。

       Register和local memory是線程私有的。Shared memory是同一個block中的線程共享的。

       Table 1顯示了cuda聲明變量的語法。Scope表示變量能夠被訪問的線程范圍。包括thread:線程單獨訪問,每個thread都有一個變量,如果kernel聲明一個scope為thread的變量y,在啟動x個線程后,就會有x個版本的變量y。block:被block中的所有thread訪問,grid:被grid中的所有線程訪問。

       Lifetime是變量的生存期。注意:如果生存期為kernel,那么在kernel不同的啟動之間,變量的值是不會被保存下來的。每次啟動一次kernel都要對變量進行初始化。生存期為application的變量,必須在所有函數體外進行聲明,變量可以在程序執行中保存下來並可以被所有kernel訪問。

 

       非數組自動變量:除了在kernel和device函數中聲明的數組外,其他所有自動變量都在寄存器中。這些變量稱為scalar變量,scope是單獨的線程。當一個kernel聲明了一個自動變量,系統會為執行這個kernel函數的所有線程copy這個變量。線程終止后,所有變量也就不存在了。

       自動數組變量:存在global memory中,對它們的訪問需要長延遲。他們的scope也是單獨的線程。因此,對這種變量盡量避免使用。

(__device__)__shared__修飾的變量,表示CUDA中的共享變量。共享變量的scope是block,block中的所有線程都可以看到共享變量的同一個版本。Lifetime是kernel,kernel結束,共享變量內存也就不存在了。對共享內存的訪問非常快而且是高度並行的。CUDA編程者通常用共享內存來保留一部分在kernel中用的多的全局內存數據。

       (__device__) __constant__修飾的變量表示常數變量constant variable。Constant variable必須在函數體外進行聲明。Scope是grids, lifetime是整個應用程序的執行。Constant variable常用於為kernel function提供輸入值,存儲在global memory中但被cached。一個程序constant variable最大可以使65536個字節。

       __device__修飾的變量是global variable,存儲在global memory中。對global memory的訪問非常慢。由於global variable對所有kernel中的所有線程都是可見的。因此,global variable可以作為跨block的線程之間的協同方法。但是,如果不終止目前的kernel,無法保證線程之間數據的一致性。因此,global variable通常作為kernel function之間的信息傳遞。

       指針只能用於指向global memory的數據對象,不用於device memory。指針有兩種典型用法:第一,如果一個對象由host function分配,指向此對象的指針被cudaMalloc()初始化並能夠作為參數傳遞給kernel function。第二,在global memory中聲明的變量的地址可以分配給一個指針變量。例如,

float * ptr=&GlobalVar。

減少全局內存通信的策略

       由於全局內存大而慢,共享內存小而快。常用的策略是把數據划分成片tile,每一片適合共享內存的使用。對這些tile的kernel計算可以獨立的進行。

__global__ void MatrixMulKernel(float* Md, float* Nd, float* Pd, int Width)

{

1. __shared__float Mds[TILE_WIDTH][TILE_WIDTH];

2. __shared__float Nds[TILE_WIDTH][TILE_WIDTH];

3. int bx = blockIdx.x; int by = blockIdx.y;

4. int tx = threadIdx.x; int ty = threadIdx.y;

// Identify the row and column of the Pd element to work on

5. int Row = by * TILE_WIDTH + ty;

6. int Col = bx * TILE_WIDTH + tx;

7. float Pvalue = 0;

// Loop over the Md and Nd tiles required to compute the Pd element

8. for (int m = 0; m < Width/TILE_WIDTH; ++m) {

// Coolaborative loading of Md and Nd tiles into shared memory

9.           Mds[ty][tx] = Md[Row][m*TILE_WIDTH + tx];

10.          Nds[ty][tx] = Nd[m*TILE_WIDTH + ty][Col];

11.         __Syncthreads();

12.         for (int k = 0; k < TILE_WIDTH; ++k)

13.                Pvalue += Mds[ty][k] * Nds[k][tx];

14. }

15. Pd[Row][Col] = Pvalue;

}

 

硬件限制:

       GeForce 8800GTX每個SM有8K個寄存器,整個處理器有128K個寄存器。一個SM最多有768個線程。如果要達到這個線程最大數,每個線程只能用8K/768=10個寄存器。如果每個線程要用11個寄存器,那么線程數就會減少。例如,如果一個block有256個線程,那么每個SM中只有1/3的線程同時存在。

       共享內存也會限制線程數目。在GeForce 8800 GTX中,每個SM有16K bytes大小的共享內存。而共享內存是block使用的。每個SM最多有8個block,所以,如果一個SM中有8個block,那么每個block最多能夠使用2K字節的共享內存。以矩陣乘為例,若tile大小為16*16,那么,每個block需要16*16*4=1K字節存儲Mds,需要1K字節存儲Nds。因此,一個block需要2K字節的共享內存。根據共享內存16K 字節的限制,最多有8個block可以同時存在於一個SM中,這也是硬件限制上的最大block數目了。若tile size是32*32,那么每個block需要8K 字節共享內存,那么一個SM只能有2個block。

 

注意:不時關注CUDA主頁,關注CUDA相關的最新研究

http://www.nvidia.com/object/cuda_home.html#


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM