311
技術社區[雲棲]
CUDA從入門到精通(四):加深對設備的認識
前麵三節已經對CUDA做了一個簡單的介紹,這一節開始真正進入編程環節。
首先,初學者應該對自己使用的設備有較為紮實的理解和掌握,這樣對後麵學習並行程序優化很有幫助,了解硬件詳細參數可以通過上節介紹的幾本書和官方資料獲得,但如果仍然覺得不夠直觀,那麼我們可以自己動手獲得這些內容。
以第二節例程為模板,我們稍加改動的部分代碼如下:
// Add vectors in parallel.
cudaError_t cudaStatus;
int num = 0;
cudaDeviceProp prop;
cudaStatus = cudaGetDeviceCount(&num);
for(int i = 0;i<num;i++)
{
cudaGetDeviceProperties(&prop,i);
}
cudaStatus = addWithCuda(c, a, b, arraySize);
這個改動的目的是讓我們的程序自動通過調用cuda API函數獲得設備數目和屬性,所謂“知己知彼,百戰不殆”。
cudaError_t 是cuda錯誤類型,取值為整數。
cudaDeviceProp為設備屬性結構體,其定義可以從cuda Toolkit安裝目錄中找到,我的路徑為:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v5.0\include\driver_types.h,找到定義為:
/**
* CUDA device properties
*/
struct __device_builtin__ cudaDeviceProp
{
char name[256]; /**< ASCII string identifying device */
size_t totalGlobalMem; /**< Global memory available on device in bytes */
size_t sharedMemPerBlock; /**< Shared memory available per block in bytes */
int regsPerBlock; /**< 32-bit registers available per block */
int warpSize; /**< Warp size in threads */
size_t memPitch; /**< Maximum pitch in bytes allowed by memory copies */
int maxThreadsPerBlock; /**< Maximum number of threads per block */
int maxThreadsDim[3]; /**< Maximum size of each dimension of a block */
int maxGridSize[3]; /**< Maximum size of each dimension of a grid */
int clockRate; /**< Clock frequency in kilohertz */
size_t totalConstMem; /**< Constant memory available on device in bytes */
int major; /**< Major compute capability */
int minor; /**< Minor compute capability */
size_t textureAlignment; /**< Alignment requirement for textures */
size_t texturePitchAlignment; /**< Pitch alignment requirement for texture references bound to pitched memory */
int deviceOverlap; /**< Device can concurrently copy memory and execute a kernel. Deprecated. Use instead asyncEngineCount. */
int multiProcessorCount; /**< Number of multiprocessors on device */
int kernelExecTimeoutEnabled; /**< Specified whether there is a run time limit on kernels */
int integrated; /**< Device is integrated as opposed to discrete */
int canMapHostMemory; /**< Device can map host memory with cudaHostAlloc/cudaHostGetDevicePointer */
int computeMode; /**< Compute mode (See ::cudaComputeMode) */
int maxTexture1D; /**< Maximum 1D texture size */
int maxTexture1DMipmap; /**< Maximum 1D mipmapped texture size */
int maxTexture1DLinear; /**< Maximum size for 1D textures bound to linear memory */
int maxTexture2D[2]; /**< Maximum 2D texture dimensions */
int maxTexture2DMipmap[2]; /**< Maximum 2D mipmapped texture dimensions */
int maxTexture2DLinear[3]; /**< Maximum dimensions (width, height, pitch) for 2D textures bound to pitched memory */
int maxTexture2DGather[2]; /**< Maximum 2D texture dimensions if texture gather operations have to be performed */
int maxTexture3D[3]; /**< Maximum 3D texture dimensions */
int maxTextureCubemap; /**< Maximum Cubemap texture dimensions */
int maxTexture1DLayered[2]; /**< Maximum 1D layered texture dimensions */
int maxTexture2DLayered[3]; /**< Maximum 2D layered texture dimensions */
int maxTextureCubemapLayered[2];/**< Maximum Cubemap layered texture dimensions */
int maxSurface1D; /**< Maximum 1D surface size */
int maxSurface2D[2]; /**< Maximum 2D surface dimensions */
int maxSurface3D[3]; /**< Maximum 3D surface dimensions */
int maxSurface1DLayered[2]; /**< Maximum 1D layered surface dimensions */
int maxSurface2DLayered[3]; /**< Maximum 2D layered surface dimensions */
int maxSurfaceCubemap; /**< Maximum Cubemap surface dimensions */
int maxSurfaceCubemapLayered[2];/**< Maximum Cubemap layered surface dimensions */
size_t surfaceAlignment; /**< Alignment requirements for surfaces */
int concurrentKernels; /**< Device can possibly execute multiple kernels concurrently */
int ECCEnabled; /**< Device has ECC support enabled */
int pciBusID; /**< PCI bus ID of the device */
int pciDeviceID; /**< PCI device ID of the device */
int pciDomainID; /**< PCI domain ID of the device */
int tccDriver; /**< 1 if device is a Tesla device using TCC driver, 0 otherwise */
int asyncEngineCount; /**< Number of asynchronous engines */
int unifiedAddressing; /**< Device shares a unified address space with the host */
int memoryClockRate; /**< Peak memory clock frequency in kilohertz */
int memoryBusWidth; /**< Global memory bus width in bits */
int l2CacheSize; /**< Size of L2 cache in bytes */
int maxThreadsPerMultiProcessor;/**< Maximum resident threads per multiprocessor */
};
後麵的注釋已經說明了其字段代表意義,可能有些術語對於初學者理解起來還是有一定困難,沒關係,我們現在隻需要關注以下幾個指標:
name:就是設備名稱;
totalGlobalMem:就是顯存大小;
major,minor:CUDA設備版本號,有1.1, 1.2, 1.3, 2.0, 2.1等多個版本;
clockRate:GPU時鍾頻率;
multiProcessorCount:GPU大核數,一個大核(專業點稱為流多處理器,SM,Stream-Multiprocessor)包含多個小核(流處理器,SP,Stream-Processor)
編譯,運行,我們在VS2008工程的cudaGetDeviceProperties()函數處放一個斷點,單步執行這一函數,然後用Watch窗口,切換到Auto頁,展開+,在我的筆記本上得到如下結果:

可以看到,設備名為GeForce 610M,顯存1GB,設備版本2.1(比較高端了,哈哈),時鍾頻率為950MHz(注意950000單位為kHz),大核數為1。在一些高性能GPU上(如Tesla,Kepler係列),大核數可能達到幾十甚至上百,可以做更大規模的並行處理。
PS:今天看SDK代碼時發現在help_cuda.h中有個函數實現從CUDA設備版本查詢相應大核中小核的數目,覺得很有用,以後編程序可以借鑒,摘抄如下:
// Beginning of GPU Architecture definitions
inline int _ConvertSMVer2Cores(int major, int minor)
{
// Defines for GPU Architecture types (using the SM version to determine the # of cores per SM
typedef struct
{
int SM; // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version
int Cores;
} sSMtoCores;
sSMtoCores nGpuArchCoresPerSM[] =
{
{ 0x10, 8 }, // Tesla Generation (SM 1.0) G80 class
{ 0x11, 8 }, // Tesla Generation (SM 1.1) G8x class
{ 0x12, 8 }, // Tesla Generation (SM 1.2) G9x class
{ 0x13, 8 }, // Tesla Generation (SM 1.3) GT200 class
{ 0x20, 32 }, // Fermi Generation (SM 2.0) GF100 class
{ 0x21, 48 }, // Fermi Generation (SM 2.1) GF10x class
{ 0x30, 192}, // Kepler Generation (SM 3.0) GK10x class
{ 0x35, 192}, // Kepler Generation (SM 3.5) GK11x class
{ -1, -1 }
};
int index = 0;
while (nGpuArchCoresPerSM[index].SM != -1)
{
if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor))
{
return nGpuArchCoresPerSM[index].Cores;
}
index++;
}
// If we don't find the values, we default use the previous one to run properly
printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[7].Cores);
return nGpuArchCoresPerSM[7].Cores;
}
// end of GPU Architecture definitions
可見,設備版本2.1的一個大核有48個小核,而版本3.0以上的一個大核有192個小核!
前文說到過,當我們用的電腦上有多個顯卡支持CUDA時,怎麼來區分在哪個上運行呢?這裏我們看一下addWithCuda這個函數是怎麼做的。
cudaError_t cudaStatus;
// Choose which GPU to run on, change this on a multi-GPU system.
cudaStatus = cudaSetDevice(0);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
goto Error;
}
使用了cudaSetDevice(0)這個操作,0表示能搜索到的第一個設備號,如果有多個設備,則編號為0,1,2...。
再看我們本節添加的代碼,有個函數cudaGetDeviceCount(&num),這個函數用來獲取設備總數,這樣我們選擇運行CUDA程序的設備號取值就是0,1,...num-1,於是可以一個個枚舉設備,利用cudaGetDeviceProperties(&prop)獲得其屬性,然後利用一定排序、篩選算法,找到最符合我們應用的那個設備號opt,然後調用cudaSetDevice(opt)即可選擇該設備。選擇標準可以從處理能力、版本控製、名稱等各個角度出發。後麵講述流並發過程時,還要用到這些API。
如果希望了解更多硬件內容可以結合https://www.geforce.cn/hardware獲取。
最後更新:2017-04-03 16:48:43