#include <stdio.h> #include "common/cpu_bitmap.h" //是否使用__constan__常量內存的開關 #define CONSTANT #define INF 2e10f #define rnd(x) (x*rand()/RAND_MAX) #define SPHERES 200 #define DIM 800 //求結構體 struct Sphere { float r, g, b; //球的顏色 float radius; //球的半徑 float x, y, z; //球心座標 //判斷從像素點(ox, oy)射出的射線是否與該球相交,並返回交點的z座標 __device__ float
hit( float ox, float
oy, float *n) { float dx = ox - x; float dy = oy - y; if (dx*dx + dy*dy < radius*radius) { float dz = sqrtf(radius*radius - dx*dx - dy*dy); *n = dz/sqrtf(radius*radius); return dz + z; } return -INF; } }; #ifdef CONSTANT //__constant__ 常量內存 __constant__ Sphere s[SPHERES]; #else Sphere *s; #endif #ifdef CONSTANT //使用constant常量內存時,不能將其當作參數傳到global函數 __global__ void
kernel(unsigned char
* ptr) #else //普通全局變量必須用傳參的形式傳遞到global函數 __global__ void
kernel(unsigned char
* ptr, Sphere *s) #endif { int x = threadIdx.x + blockIdx.x * blockDim.x; int y = threadIdx.y + blockIdx.y * blockDim.y; int offset = x + y * blockDim.x * gridDim.x; float ox = (x - DIM/2); float oy = (y - DIM/2); float r=0,g=0,b=0; //獲得最近的交點 float maxz = -INF; for ( int
i=0; i<SPHERES; i++) { float n; float t = s[i].hit(ox, oy, &n); if (t>maxz) { float fscale = n; r = s[i].r * fscale; g = s[i].g * fscale; b = s[i].b * fscale; maxz = t; } } ptr[offset*4 + 0] = ( int )(r*255); ptr[offset*4 + 1] = ( int )(g*255); ptr[offset*4 + 2] = ( int )(b*255); ptr[offset*4 + 3] = 255; } int main( void ) { //使用cuda事件來測試性能 cudaEvent_t start, stop; cudaEventCreate(&start); cudaEventCreate(&stop); cudaEventRecord(start, 0); CPUBitmap bitmap(DIM, DIM); unsigned char * dev_bitmap; cudaMalloc(( void **) &dev_bitmap, bitmap.image_size()); #ifdef CONSTANT // __constant__常量內存不需要動態分配內存 #else // 在GPU設備上分配內存給球數組 cudaMalloc(( void **) &s,
sizeof (Sphere) * SPHERES); #endif // 在CPU上生成求數據數據 Sphere *temp_s = (Sphere *) malloc (
sizeof (Sphere) * SPHERES); for ( int
i=0; i<SPHERES; i++) { temp_s[i].r = rnd(1.0f); temp_s[i].g = rnd(1.0f); temp_s[i].b = rnd(1.0f); temp_s[i].x = rnd(1000.f) - 500; temp_s[i].y = rnd(1000.f) - 500; temp_s[i].z = rnd(1000.f) - 500; temp_s[i].radius = rnd(100.f) + 20; } #ifdef CONSTANT //從CPU拷貝到__constant__常量內存 cudaMemcpyToSymbol(s, temp_s, sizeof (Sphere) * SPHERES); #else //從CPU拷貝到GPU cudaMemcpy(s, temp_s, sizeof (Sphere) * SPHERES, cudaMemcpyHostToDevice); #endif free (temp_s); dim3 grids(DIM/16, DIM/16); dim3 threads(16, 16); #ifdef CONSTANT kernel<<<grids, threads>>>(dev_bitmap); #else kernel<<<grids, threads>>>(dev_bitmap, s); #endif cudaMemcpy(bitmap.get_ptr(), dev_bitmap, bitmap.image_size(), cudaMemcpyDeviceToHost); cudaEventRecord(stop, 0); //事件同步 cudaEventSynchronize(stop); float elapseTime; cudaEventElapsedTime(&elapseTime, start, stop); printf ( "Time to generate: %3.1f ms\n" , elapseTime); cudaEventDestroy(start); cudaEventDestroy(stop); bitmap.display_and_exit(); cudaFree(dev_bitmap); #ifdef CONSTANT // __constant__ 常量內存不需要free #else cudaFree(s); #endif return 1; } 與標準的全局常量內存相比,常量內存存在着一些限制,但在某些情況中,實用常量內存將提升應用程序的性能。
特別是,當線程束中的所有線程都訪問相同的只讀數據時,將獲得額外的性能提升。在這種數據訪問模式中實用常量
內存可以節約內存帶寬,不僅是因爲這種模式可以將讀取操作在半線程束中廣播,而且還因爲在芯片上包含了常量內存
緩存。在許多算法中,內存帶寬都是一種瓶頸,因此採用一些機制來改善這種情況是非常有用的。
知識點:
1. 使用__constant__修飾符來聲明變量爲常量內存; 2. 常量內存爲靜態分配空間,所以不需要調用 cudaMalloc(), cudaFree();
3. CUDA 中的時間本質上是一個 GPU 時間戳,這個時間戳是在用戶指定的時間點上記錄的。
|
光線跟蹤的 GPU 程序解讀
《CUDA by example》 中的第六章講解了在 GPU 上實現光線跟蹤的一個例子,旨在介紹常量內存(constant
memory)和事件,下面給出這個例子的詳細解讀(http://code2.us/2012/02/cuda_learning_11-constant_memory_and_events/)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.