部落客由于工作當中的需要,開始學習 GPU 上面的程式設計,主要涉及到的是基于 GPU 的深度學習方面的知識,鑒于之前沒有接觸過 GPU 程式設計,是以在這裡特地學習一下 GPU 上面的程式設計。有志同道合的小夥伴,歡迎一起交流和學習,我的郵箱: [email protected] 。使用的是自己的老古董筆記本上面的 Geforce 103m 顯示卡,雖然顯示卡相對于現在主流的系列已經非常的弱,但是對于學習來說,還是可以用的。本系列博文也遵從由簡單到複雜,記錄自己學習的過程。
0. 目錄
- GPU 程式設計入門到精通(一)之 CUDA 環境安裝
- GPU 程式設計入門到精通(二)之 運作第一個程式
- GPU 程式設計入門到精通(三)之 第一個 GPU 程式
- GPU 程式設計入門到精通(四)之 GPU 程式優化
- GPU 程式設計入門到精通(五)之 GPU 程式優化進階
1. CUDA 初始化函數
由于是使用 Runtime API, 是以在檔案開頭要加入 cuda_runtime.h 頭檔案。
初始化函數包括一下幾個步驟:
1.1. 擷取 CUDA 裝置數
可以通過 cudaGetDeviceCount 函數擷取 CUDA 的裝置數,具體用法,如下所示:
// get the cuda device count
cudaGetDeviceCount(&count);
if (count == ) {
fprintf(stderr, "There is no device.\n");
return false;
}
函數通過引用傳遞 count 值,擷取目前支援的 CUDA 裝置數。
1.2. 擷取 CUDA 裝置屬性
可以通過 cudaGetDeviceProperties 函數擷取 CUDA 裝置的屬性,具體用法,如下所示:
// find the device >= 1.X
int i;
for (i = ; i < count; ++i) {
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
if (prop.major >= ) {
printDeviceProp(prop);
break;
}
}
}
// if can't find the device
if (i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
return false;
}
函數通過引用傳遞 prop 關于屬性的結構體,并且列出主裝置号大于 1 的裝置屬性,其中裝置屬性通過函數 printDeviceProp 列印。列印函數如下所示:
// function printDeviceProp
void printDeviceProp(const cudaDeviceProp &prop)
{
printf("Device Name : %s.\n", prop.name);
printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
printf("regsPerBlock : %d.\n", prop.regsPerBlock);
printf("warpSize : %d.\n", prop.warpSize);
printf("memPitch : %d.\n", prop.memPitch);
printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[], prop.maxThreadsDim[], prop.maxThreadsDim[]);
printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[], prop.maxGridSize[], prop.maxGridSize[]);
printf("totalConstMem : %d.\n", prop.totalConstMem);
printf("major.minor : %d.%d.\n", prop.major, prop.minor);
printf("clockRate : %d.\n", prop.clockRate);
printf("textureAlignment : %d.\n", prop.textureAlignment);
printf("deviceOverlap : %d.\n", prop.deviceOverlap);
printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
}
1.3. 設定 CUDA 裝置
通過函數 cudaSetDevice 就可以設定 CUDA 裝置了,具體用法,如下所示:
// set cuda device
cudaSetDevice(i);
1.4. CUDA 初始化完整代碼
/* *******************************************************************
##### File Name: first_cuda.cu
##### File Func: initial CUDA device and print device prop
##### Author: Caijinping
##### E-mail: [email protected]
##### Create Time: 2014-4-21
* ********************************************************************/
#include <stdio.h>
#include <cuda_runtime.h>
void printDeviceProp(const cudaDeviceProp &prop)
{
printf("Device Name : %s.\n", prop.name);
printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
printf("regsPerBlock : %d.\n", prop.regsPerBlock);
printf("warpSize : %d.\n", prop.warpSize);
printf("memPitch : %d.\n", prop.memPitch);
printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[], prop.maxThreadsDim[], prop.maxThreadsDim[]);
printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[], prop.maxGridSize[], prop.maxGridSize[]);
printf("totalConstMem : %d.\n", prop.totalConstMem);
printf("major.minor : %d.%d.\n", prop.major, prop.minor);
printf("clockRate : %d.\n", prop.clockRate);
printf("textureAlignment : %d.\n", prop.textureAlignment);
printf("deviceOverlap : %d.\n", prop.deviceOverlap);
printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
}
bool InitCUDA()
{
//used to count the device numbers
int count;
// get the cuda device count
cudaGetDeviceCount(&count);
if (count == ) {
fprintf(stderr, "There is no device.\n");
return false;
}
// find the device >= 1.X
int i;
for (i = ; i < count; ++i) {
cudaDeviceProp prop;
if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
if (prop.major >= ) {
printDeviceProp(prop);
break;
}
}
}
// if can't find the device
if (i == count) {
fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
return false;
}
// set cuda device
cudaSetDevice(i);
return true;
}
int main(int argc, char const *argv[])
{
if (InitCUDA()) {
printf("CUDA initialized.\n");
}
return ;
}
2. Runtime API 函數解析
2.1. cudaGetDeviceCount
cudaGetDeviceCount
傳回具有計算能力的裝置的數量
函數原型:
cudaError_t cudaGetDeviceCount( int* count )
函數說明:
以 *count 形式傳回可用于執行的計算能力大于等于 1.0 的裝置數量。如果不存在此類裝置,将傳回 1
傳回值:
cudaSuccess,注意,如果之前是異步啟動,該函數可能傳回錯誤碼。
2.2. cudaGetDeviceProperties
cudaGetDeviceProperties
傳回關于計算裝置的資訊
函數原型:
cudaError_t cudaGetDeviceProperties( struct cudaDeviceProp* prop,int dev )
函數說明:
以*prop形式傳回裝置dev的屬性。
傳回值:
cudaSuccess、cudaErrorInvalidDevice,注,如果之前是異步啟動,該函數可能傳回錯誤碼。
另外 cudaDeviceProp 結構定義如下:
struct cudaDeviceProp {
char name [];
size_t totalGlobalMem;
size_t sharedMemPerBlock;
int regsPerBlock;
int warpSize;
size_t memPitch;
int maxThreadsPerBlock;
int maxThreadsDim [];
int maxGridSize [];
size_t totalConstMem;
int major;
int minor;
int clockRate;
size_t textureAlignment;
int deviceOverlap;
int multiProcessorCount;
}
cudaDeviceProp 結構中的各個變量意義如下:
name
用于辨別裝置的ASCII字元串;
totalGlobalMem
裝置上可用的全局存儲器的總量,以位元組為機關;
sharedMemPerBlock
線程塊可以使用的共享存儲器的最大值,以位元組為機關;多處理器上的所有線程塊可以同時共享這些存儲器;
regsPerBlock
線程塊可以使用的32位寄存器的最大值;多處理器上的所有線程塊可以同時共享這些寄存器;
warpSize
按線程計算的warp塊大小;
memPitch
允許通過cudaMallocPitch()為包含存儲器區域的存儲器複制函數配置設定的最大間距(pitch),以位元組為機關;
maxThreadsPerBlock
每個塊中的最大線程數
maxThreadsDim[3]
塊各個次元的最大值:
maxGridSize[3]
網格各個次元的最大值;
totalConstMem
裝置上可用的不變存儲器總量,以位元組為機關;
major,minor
定義裝置計算能力的主要修訂号和次要修訂号;
clockRate
以千赫為機關的時鐘頻率;
textureAlignment
對齊要求;與textureAlignment位元組對齊的紋理基址無需對紋理取樣應用偏移;
deviceOverlap
如果裝置可在主機和裝置之間并發複制存儲器,同時又能執行核心,則此值為 1;否則此值為 0;
multiProcessorCount
裝置上多處理器的數量。
2.3. cudaGetDeviceCount
cudaSetDevice
設定裝置以供GPU執行使用
函數原型:
cudaError_t cudaSetDevice(int dev)
函數說明:
将dev記錄為活動主線程将執行裝置碼的裝置。
傳回值:
cudaSuccess、cudaErrorInvalidDevice,注,如果之前是異步啟動,該函數可能傳回錯誤碼。
3. nvcc 編譯代碼
nvcc 是 CUDA 的編譯工具,它可以将 .cu 檔案解析出在 GPU 和 host 上執行的部分,也就是說,它會幫忙把 GPU 上執行和主機上執行的代碼區分開來,不許要我們手動去做了。在 GPU 執行的部分會通過 NVIDIA 提供的 編譯器編譯成中介碼,主機執行的部分則調用 gcc 編譯。
通過如下指令,可以編譯之前寫的 first_cuda.cu 程式:
nvcc -o first_cuda first_cuda.cu
通過上述編譯,生成可執行檔案 first_cuda
運作結果如下所示:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQ3chVEa0V3bT9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVPNJDWq5ESlZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TM4IzNyQDM1ETMyQDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
這一篇博文介紹了如何通過 runtime API 建立自己的第一個 CUDA 程式。通過這個程式,可以學會使用 CUDA 的一般流程。下一部分,将介紹 CUDA 如何進行 GPU 程式設計。
歡迎大家和我一起讨論和學習 GPU 程式設計。
[email protected]
http://blog.csdn.net/xsc_c