我對類賦值有一個簡單的CUDA問題,但教授添加了一個可選任務來實現使用共享內存的相同算法。我無法在截止日期之前完成它(如轉機日期是一週前),但我仍然好奇,所以現在我要問網絡了)。CUDA:通過大型2D陣列共享內存
基本的任務是實現連續過度放鬆和CUDA過程中的混合版本的連續過度放鬆,確保你在兩者中得到相同的結果,然後比較加速比。就像我說的那樣,使用共享內存做一個可選的+ 10%附加組件。
我打算髮布我的工作版本和僞代碼,因爲我目前沒有手中的代碼,但如果有人需要,我可以稍後用實際代碼更新此代碼它。
之前有人說:是的,我知道使用CUtil是跛腳,但它使比較和計時器更容易。
工作全局存儲器版本:
#include <stdlib.h>
#include <stdio.h>
#include <cutil_inline.h>
#define N 1024
__global__ void kernel(int *d_A, int *d_B) {
unsigned int index_x = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int index_y = blockIdx.y * blockDim.y + threadIdx.y;
// map the two 2D indices to a single linear, 1D index
unsigned int grid_width = gridDim.x * blockDim.x;
unsigned int index = index_y * grid_width + index_x;
// check for boundaries and write out the result
if((index_x > 0) && (index_y > 0) && (index_x < N-1) && (index_y < N-1))
d_B[index] = (d_A[index-1]+d_A[index+1]+d_A[index+N]+d_A[index-N])/4;
}
main (int argc, char **argv) {
int A[N][N], B[N][N];
int *d_A, *d_B; // These are the copies of A and B on the GPU
int *h_B; // This is a host copy of the output of B from the GPU
int i, j;
int num_bytes = N * N * sizeof(int);
// Input is randomly generated
for(i=0;i<N;i++) {
for(j=0;j<N;j++) {
A[i][j] = rand()/1795831;
//printf("%d\n",A[i][j]);
}
}
cudaEvent_t start_event0, stop_event0;
float elapsed_time0;
CUDA_SAFE_CALL(cudaEventCreate(&start_event0));
CUDA_SAFE_CALL(cudaEventCreate(&stop_event0));
cudaEventRecord(start_event0, 0);
// sequential implementation of main computation
for(i=1;i<N-1;i++) {
for(j=1;j<N-1;j++) {
B[i][j] = (A[i-1][j]+A[i+1][j]+A[i][j-1]+A[i][j+1])/4;
}
}
cudaEventRecord(stop_event0, 0);
cudaEventSynchronize(stop_event0);
CUDA_SAFE_CALL(cudaEventElapsedTime(&elapsed_time0,start_event0, stop_event0));
h_B = (int *)malloc(num_bytes);
memset(h_B, 0, num_bytes);
//ALLOCATE MEMORY FOR GPU COPIES OF A AND B
cudaMalloc((void**)&d_A, num_bytes);
cudaMalloc((void**)&d_B, num_bytes);
cudaMemset(d_A, 0, num_bytes);
cudaMemset(d_B, 0, num_bytes);
//COPY A TO GPU
cudaMemcpy(d_A, A, num_bytes, cudaMemcpyHostToDevice);
// create CUDA event handles for timing purposes
cudaEvent_t start_event, stop_event;
float elapsed_time;
CUDA_SAFE_CALL(cudaEventCreate(&start_event));
CUDA_SAFE_CALL(cudaEventCreate(&stop_event));
cudaEventRecord(start_event, 0);
// TODO: CREATE BLOCKS AND THREADS AND INVOKE GPU KERNEL
dim3 block_size(256,1,1); //values experimentally determined to be fastest
dim3 grid_size;
grid_size.x = N/block_size.x;
grid_size.y = N/block_size.y;
kernel<<<grid_size,block_size>>>(d_A,d_B);
cudaEventRecord(stop_event, 0);
cudaEventSynchronize(stop_event);
CUDA_SAFE_CALL(cudaEventElapsedTime(&elapsed_time,start_event, stop_event));
//COPY B BACK FROM GPU
cudaMemcpy(h_B, d_B, num_bytes, cudaMemcpyDeviceToHost);
// Verify result is correct
CUTBoolean res = cutComparei((int *)B, (int *)h_B, N*N);
printf("Test %s\n",(1 == res)?"Passed":"Failed");
printf("Elapsed Time for Sequential: \t%.2f ms\n", elapsed_time0);
printf("Elapsed Time for CUDA:\t%.2f ms\n", elapsed_time);
printf("CUDA Speedup:\t%.2fx\n",(elapsed_time0/elapsed_time));
cudaFree(d_A);
cudaFree(d_B);
free(h_B);
cutilDeviceReset();
}
共享內存的版本,這是我到目前爲止已經試過:
#define N 1024
__global__ void kernel(int *d_A, int *d_B, int width) {
//assuming width is 64 because that's the biggest number I can make it
//each MP has 48KB of shared mem, which is 12K ints, 32 threads/warp, so max 375 ints/thread?
__shared__ int A_sh[3][66];
//get x and y index and turn it into linear index
for(i=0; i < width+2; i++) //have to load 2 extra values due to the -1 and +1 in algo
A_sh[index_y%3][i] = d_A[index+i-1]; //so A_sh[index_y%3][0] is actually d_A[index-1]
__syncthreads(); //and hope that previous and next row have been loaded by other threads in the block?
//ignore boundary conditions because it's pseudocode
for(i=0; i < width; i++)
d_B[index+i] = A_sh[index_y%3][i] + A_sh[index_y%3][i+2] + A_sh[index_y%3-1][i+1] + A_sh[index_y%3+1][i+1];
}
main(){
//same init as above until threads/grid init
dim3 threadsperblk(32,16);
dim3 numblks(32,64);
kernel<<<numblks,threadsperblk>>>(d_A,d_B,64);
//rest is the same
}
此共享MEM代碼崩潰(「因發射失敗未指定的錯誤「),因爲我還沒有捕捉到所有的邊界條件,但我並不擔心這一點,而是尋找正確的方式來推動事情發展。我覺得我的代碼太複雜了,不能成爲正確的路徑(特別是與SDK例子相比),但是我也看不到另一種方法來實現它,因爲我的數組不像所有例子那樣適合共享mem可以找到。
坦率地說,我不知道這將是我的硬件要快得多(GTX 560 TI - 運行在0.121ms全局內存版本),但我需要首先證明自己:P
編輯2:對於任何在將來運行的人來說,如果你想做一些共享內存,答案中的代碼是一個很好的起點。
沒想到這樣的說法,謝謝。問題是,我該如何阻止街區內的線路互相走動?假設我在一個塊中有2個線程,並且線程2想要在線程1仍然在行(c)上工作時加載行(f)?或者我應該改變代碼使每個塊有1個線程,然後有幾個塊? – a5ehren 2011-04-27 18:06:32
@ a5ehren:有一個叫做__syncthreads()的塊內同步原語,可以用來同步線程。理想情況下,您需要每塊32個線程的多個循環,並且需要覆蓋輸入空間的行寬所需的塊數。如果您需要更多幫助,我可以在答案中添加一些小的僞代碼。 – talonmies 2011-04-27 18:49:02
所以我會讓每個線程加載其行的一部分,同步它,然後假設有線程處理上面和下面的行?我猜有些僞代碼將有助於:P – a5ehren 2011-04-27 20:50:58