第三章 第一段CUDA C代碼+Host/Device
3.1 第一個程序
3.1.1 Hello,World!
#include "../common/book.h"
int main( void ) {
printf( "Hello, World!\n" );
return 0;
}
我們將CPU以及系統的內存稱爲主機,而將GPU及其內存稱爲設備。這個示例程序與你編寫過的代碼非常相似,因爲它並不考慮主機之外的任何計算設備。
爲了避免使你產生一無所獲的感覺,我們將逐漸完善這個簡單示例。我們來看看如何使用GPU(這就是一個設備)來執行代碼。在GPU設備上執行的函數通常稱爲核函數( Kernel)。
3.1.2 核函數調用
#include "../common/book.h"
<span style="color:#ff0000;background-color: rgb(255, 255, 51);">__global__ void kernel( void ) {
}</span>
int main( void ) {
<span style="color:#ff0000;background-color: rgb(255, 255, 51);">kernel<<<1,1>>>();</span>
printf( "Hello, World!\n" );
return 0;
}
這個程序與最初的“ Hello, World!”相比,多了兩個值得注意的地方:
• 一個空的函數kernel(),並且帶有修飾符__global__。
• 對這個空函數的調用,並且帶有修飾字符<<<1,1>>>。
3.1.3 傳遞參數
#include "../common/book.h"
<span style="color:#ff0000;background-color: rgb(255, 255, 51);">__global__ void add( int a, int b, int *c ) {
*c = a + b;
}</span>
int main( void ) {
int c;
int *dev_c;
<span style="background-color: rgb(255, 255, 51);">HANDLE_ERROR( cudaMalloc( (void**)&dev_c, sizeof(int) ) );</span>
<span style="color:#ff0000;background-color: rgb(255, 255, 51);">add<<<1,1>>>( 2, 7, dev_c );</span>
<span style="background-color: rgb(255, 255, 51);">HANDLE_ERROR( cudaMemcpy( &c, dev_c, sizeof(int),
cudaMemcpyDeviceToHost ) );</span>
printf( "2 + 7 = %d\n", c );
<span style="background-color: rgb(255, 255, 51);"> HANDLE_ERROR( cudaFree( dev_c ) );</span>
return 0;
}
注意這裏增加了多行代碼,在這些代碼中包含兩個概念:
• 可以像調用C函數那樣將參數傳遞給核函數。
• 當設備執行任何有用的操作時,都需要分配內存,例如將計算值返回給主機。
- 第一個參數是一個指針,指向用於保存新分配內存地址的變量,
- 第二個參數是分配內存的大小。除了分配內存的指針不是作爲函數的返回值外,這個函數的行爲與malloc()是相同的,並且返回類型爲void*。
- 可以將cudaMalloc()分配的指針傳遞給在設備上執行的函數。
- 可以在設備代碼中使用cudaMalloc()分配的指針進行內存讀/寫操作。
- 可以將cudaMalloc()分配的指針傳遞給在主機上執行的函數。
- 不能在主機代碼中使用cudaMalloc()分配的指針進行內存讀/寫操作。
3.2 查詢設備
int count;
HANDLE_ERROR( cudaGetDeviceCount( &count ) );
struct cudaDeviceProp
{
char name[256];
size_t totalGlobalMem;
size_t sharedMemPerBlock;
int regsPerBlock;
int warpSize;
size_t memPitch;
int maxThreadsPerBlock;
int maxThreadsDim[3];
int maxGridSize[3];
size_t totalConstMem;
int major;
int minor;
int clockRate;
size_t textureAlignment;
int deviceOverlap;
int multiProcessorCount;
int kernelExecTimeoutEnabled;
int integrated;
int canMapHostMemory;
int computeMode;
int maxTexture1D;
int maxTexture2D[2];
int maxTexture3D[3];
int maxTexture2DArray[3];
int concurrentKernels;
}
其中,有些屬性的含義是顯而易見的,其他屬性的含義如下所示(見表3.1)。
#include "../common/book.h"
int main( void ) {
<span style="background-color: rgb(255, 255, 51);">cudaDeviceProp <span style="color:#ff0000;">prop</span>;</span>
int dev;
HANDLE_ERROR( <span style="background-color: rgb(255, 255, 51);">cudaGetDevice( &dev ) </span>);
printf( "ID of current CUDA device: %d\n", dev );
memset( &prop, 0, sizeof( cudaDeviceProp ) );
prop.major = 1;
prop.minor = 3;
HANDLE_ERROR( <span style="background-color: rgb(255, 255, 51);">cudaChooseDevice</span>( &dev, &prop ) );
printf( "ID of CUDA device closest to revision 1.3: %d\n", dev );
HANDLE_ERROR( <span style="background-color: rgb(255, 255, 51);">cudaSetDevice</span>( dev ) );
}
#include "../common/book.h"
int main( void ) {
cudaDeviceProp prop;
int count;
HANDLE_ERROR( cudaGetDeviceCount( &count ) );
for (int i=0; i< count; i++)
{
HANDLE_ERROR( cudaGetDeviceProperties( &prop, i ) );
printf( " --- General Information for device %d ---\n", i );
printf( "Name: %s\n", prop.name );
printf( "Compute capability: %d.%d\n", prop.major, prop.minor );
printf( "Clock rate: %d\n", prop.clockRate );
printf( "Device copy overlap: " );
if (prop.deviceOverlap)
printf( "Enabled\n" );
else
printf( "Disabled\n");
printf( "Kernel execution timeout : " );
if (prop.kernelExecTimeoutEnabled)
printf( "Enabled\n" );
else
printf( "Disabled\n" );
printf( " --- Memory Information for device %d ---\n", i );
printf( "Total global mem: %ld\n", prop.totalGlobalMem );
printf( "Total constant Mem: %ld\n", prop.totalConstMem );
printf( "Max mem pitch: %ld\n", prop.memPitch );
printf( "Texture Alignment: %ld\n", prop.textureAlignment );
printf( " --- MP Information for device %d ---\n", i );
printf( "Multiprocessor count: %d\n",
prop.multiProcessorCount );
printf( "Shared mem per mp: %ld\n", prop.sharedMemPerBlock );
printf( "Registers per mp: %d\n", prop.regsPerBlock );
printf( "Threads in warp: %d\n", prop.warpSize );
printf( "Max threads per block: %d\n",
prop.maxThreadsPerBlock );
printf( "Max thread dimensions: (%d, %d, %d)\n",
prop.maxThreadsDim[0], prop.maxThreadsDim[1],
prop.maxThreadsDim[2] );
printf( "Max grid dimensions: (%d, %d, %d)\n",
prop.maxGridSize[0], prop.maxGridSize[1],
prop.maxGridSize[2] );
printf( "\n" );
}
}
3.3 設備屬性的使用
cudaDeviceProp prop;
memset( &prop, 0, sizeof( cudaDeviceProp ) );
prop.major = 1;
prop.minor = 3;
#include "../common/book.h"
int main( void )
{
cudaDeviceProp prop;
int dev;
HANDLE_ERROR( cudaGetDevice( &dev ) );
printf( "ID of current CUDA device: %d\n", dev );
memset( &prop, 0, sizeof( cudaDeviceProp ) );
prop.major = 1;
prop.minor = 3;
HANDLE_ERROR( cudaChooseDevice( &dev, &prop ) );
printf( "ID of CUDA device closest to revision 1.3: %d\n", dev );
HANDLE_ERROR( cudaSetDevice( dev ) );
}
當前,在系統中擁有多個GPU已是很常見的情況。例如,許多NVIDIA主板芯片組都包含了集成的並且支持CUDA的GPU。當把一個獨立的GPU添加到這些系統中時,那麼就形成了一個多GPU的平臺。而且, NVIDIA的SLI(Scalable Link Interface,可伸縮鏈路接口)技術使得多個獨立的GPU可以並排排列。無論是哪種情況,應用程序都可以從多個GPU中選擇最適合的GPU。如果應用程序依賴於GPU的某些特定屬性,或者需要在系統中最快的GPU上運行,那麼你就需要熟悉這個API,因爲CUDA運行時本身並不能保證爲應用程序選擇最優或者最合適的GPU。