2012-08-16 4 views
3

일부 표준 문제를 해결하기 위해 CUDA를 배우고 있습니다. 예를 들어, 다음 코드로 확산 방정식을 2 차원으로 풀고 있습니다. 그러나 내 결과는 표준 결과와 다르며이를 파악할 수는 없습니다.CUDA를 이용한 2 차원 확산 (열) 방정식

//kernel definition 
__global__ void diffusionSolver(double* A, double * old,int n_x,int n_y) 
{ 

    int i = blockIdx.x * blockDim.x + threadIdx.x; 
    int j = blockIdx.y * blockDim.y + threadIdx.y; 

    if(i*(n_x-i-1)*j*(n_y-j-1)!=0) 
     A[i+n_y*j] = A[i+n_y*j] + (old[i-1+n_y*j]+old[i+1+n_y*j]+ 
         old[i+(j-1)*n_y]+old[i+(j+1)*n_y] -4*old[i+n_y*j])/40; 


} 

int main() 
{ 


    int i,j ,M; 
    M = n_y ; 
    phi = (double *) malloc(n_x*n_y* sizeof(double)); 
    phi_old = (double *) malloc(n_x*n_y* sizeof(double)); 
    dummy = (double *) malloc(n_x*n_y* sizeof(double)); 
    int iterationMax =10; 
    //phase initialization 
    for(j=0;j<n_y ;j++) 
    { 
     for(i=0;i<n_x;i++) 
     { 
      if((.4*n_x-i)*(.6*n_x-i)<0) 
       phi[i+M*j] = -1; 
      else 
       phi[i+M*j] = 1; 

      phi_old[i+M*j] = phi[i+M*j]; 
     } 
    } 

    double *dev_phi; 
    cudaMalloc((void **) &dev_phi, n_x*n_y*sizeof(double)); 

    dim3 threadsPerBlock(100,10); 
    dim3 numBlocks(n_x*n_y/threadsPerBlock.x, n_x*n_y/threadsPerBlock.y); 

    //start iterating 
    for(int z=0; z<iterationMax; z++) 
    { 
     //copy array on host to device 
     cudaMemcpy(dev_phi, phi, n_x*n_y*sizeof(double), 
       cudaMemcpyHostToDevice); 

     //call kernel 
     diffusionSolver<<<numBlocks, threadsPerBlock>>>(dev_phi, phi_old,n_x,n_y); 

     //get updated array back on host 
     cudaMemcpy(phi, dev_phi,n_x*n_y*sizeof(double), cudaMemcpyDeviceToHost); 

     //old values will be assigned new values 
     for(j=0;j<n_y ;j++) 
     { 
      for(i=0;i<n_x;i++) 
      { 
       phi_old[i+n_y*j] = phi[i+n_y*j]; 
      } 
     } 
    } 

    return 0; 
} 

누군가이 프로세스에 이상이있을 경우 알려 줄 수 있습니까? 어떤 도움이라도 대단히 감사하겠습니다. 여기

+2

"이전 값에 새 값이 할당 됨"섹션은 무의미합니다. device-to-device memcpy를 수행하고 전송 및 호스트 측 루프를 제거하거나 더 나은 방법으로 포인터 값을 바꿀 수 있습니다. – talonmies

+0

@talonmies, 제안에 감사드립니다. 나는 포인터 값을 바꿀 것이다. (쉬운 것 같다.) – chatur

+1

코드에서 n_x와 n_y의 값이 무엇인지 아무 곳에서도 말하지 않고 코드에서 오류 검사를 수행하고있다. 모든 CUDA API 호출은 상태를 반환합니다.커널을 실제로 실행하고 코드가 올바르게 실행되는지 확인하려면 모두 확인해야합니다. – talonmies

답변

3

큰 실수 중 하나는 phi_old가 커널에 전달되어 커널에 의해 사용되지만 이것은 호스트 포인터입니다.
Malloc a cudaMalloc을 사용하여 dev_phi_old. 기본값으로 설정하고 z 루프에 들어가기 전에 처음으로 GPU에 복사하십시오.

+0

실수로 커널 호출에 여분의 조건을 넣어야한다고 지적했습니다. (if (i chatur

+1

이유는 범위를 벗어난 색인 생성으로 이어질 스레드를 필터링해야하기 때문입니다. 때로는 주어진 차원 n_x 및 n_y에 대해 X 및 Y 스레드의 양을 정확히 시작할 수 없습니다. – brano

2

: 잘못된 확산 속도가 발생할 수 있습니다

A[i+n_y*j] = A[i+n_y*j] + (old[i-1+n_y*j]+old[i+1+n_y*j]+old[i+(j-1)*n_y]+old[i+(j+1)*n_y] -4*old[i+n_y*j])/40; 

당신은 40 (정수)로 나누어 있습니다. 실제로 확산되지 않을 수 있습니다.

하지만 A는 복식의 배열입니다.

확산율을 40.0으로 나누고 작동하는지 확인하십시오. 여기

-4*old[i+n_y*j])/40; 

당신이 4 (정수)로 곱된다 :이 호세 - 스탐의 해석에서 인 경우

, 그렇지 40

Theres는 또 다른 일이 4.0이어야한다. 이로 인해 일체형 주조가 발생할 수도 있습니다!

이 :

-4.0*old[i+n_y*j])/40.0; 

는 약간의 오차가 감소합니다.

좋은 하루 보내십시오.

+0

답장 tugrul을 보내 주셔서 감사합니다. 40.0으로 시도했지만 결과는 여전히 다릅니다 (백분율 오차 ~ 40). 나는 수치 적으로 안정하다는 것을 확인하기 위해 4.0 대신에 40.0을 사용하고있다. 배열을 호스트에 복사하는 절차를 살펴보고 (그리고 그 반대의 경우) 특히 이것이 여러 번 수행해야하는 올바른 방법인지 확인하십시오. – chatur

+0

당신이 말했듯이 내 배열 값은 전혀 변하지 않습니다 .. !! 나는 당신의 솔루션을 시도했지만 그것도 작동하지 않습니다. 커널을 실제로 호출 할 수 있도록 모든 방법을 제안 할 수 있습니까? – chatur

+0

괜찮 았어, 그것에 노력하고있어 –

2

talonmies, brano 및 huseyin은 이미 코드의 실수를 지적했습니다.

확산 (열) 방정식은 CUDA로 풀 수있는 편미분 방정식의 고전적인 예 중 하나입니다. 또한 CUDA by Example 도서의 7 장에 철저한 예제가 있습니다. 미래의 사용자에 대한 참조로

, 나는 모두 CPU와 GPU 코드를 포함하여 전체 가공 한 예를 아래에 제공하고있다. talonmies에서 제안한 것처럼 포인터를 바꿔 쓰는 대신 하나의 루프에서 두 개의 Jacobi 반복을 집중시키고 있습니다.
#include <iostream> 

#include "cuda_runtime.h" 
#include "device_launch_parameters.h" 

#include "Utilities.cuh" 

#define BLOCK_SIZE_X 16 
#define BLOCK_SIZE_Y 16 

/***********************************/ 
/* JACOBI ITERATION FUNCTION - GPU */ 
/***********************************/ 
__global__ void Jacobi_Iterator_GPU(const float * __restrict__ T_old, float * __restrict__ T_new, const int NX, const int NY) 
{ 
    const int i = blockIdx.x * blockDim.x + threadIdx.x ; 
    const int j = blockIdx.y * blockDim.y + threadIdx.y ; 

           //       N 
    int P = i + j*NX;   // node (i,j)    | 
    int N = i + (j+1)*NX;  // node (i,j+1)   | 
    int S = i + (j-1)*NX;  // node (i,j-1)  W ---- P ---- E 
    int E = (i+1) + j*NX;  // node (i+1,j)   | 
    int W = (i-1) + j*NX;  // node (i-1,j)   | 
           //       S 

    // --- Only update "interior" (not boundary) node points 
    if (i>0 && i<NX-1 && j>0 && j<NY-1) T_new[P] = 0.25 * (T_old[E] + T_old[W] + T_old[N] + T_old[S]); 
} 

/***********************************/ 
/* JACOBI ITERATION FUNCTION - CPU */ 
/***********************************/ 
void Jacobi_Iterator_CPU(float * __restrict T, float * __restrict T_new, const int NX, const int NY, const int MAX_ITER) 
{ 
    for(int iter=0; iter<MAX_ITER; iter=iter+2) 
    { 
     // --- Only update "interior" (not boundary) node points 
     for(int j=1; j<NY-1; j++) 
      for(int i=1; i<NX-1; i++) { 
       float T_E = T[(i+1) + NX*j]; 
       float T_W = T[(i-1) + NX*j]; 
       float T_N = T[i + NX*(j+1)]; 
       float T_S = T[i + NX*(j-1)]; 
       T_new[i+NX*j] = 0.25*(T_E + T_W + T_N + T_S); 
      } 

     for(int j=1; j<NY-1; j++) 
      for(int i=1; i<NX-1; i++) { 
       float T_E = T_new[(i+1) + NX*j]; 
       float T_W = T_new[(i-1) + NX*j]; 
       float T_N = T_new[i + NX*(j+1)]; 
       float T_S = T_new[i + NX*(j-1)]; 
       T[i+NX*j] = 0.25*(T_E + T_W + T_N + T_S); 
      } 
    } 
} 

/******************************/ 
/* TEMPERATURE INITIALIZATION */ 
/******************************/ 
void Initialize(float * __restrict h_T, const int NX, const int NY) 
{ 
    // --- Set left wall to 1 
    for(int j=0; j<NY; j++) h_T[j * NX] = 1.0; 
} 


/********/ 
/* MAIN */ 
/********/ 
int main() 
{ 
    const int NX = 256;   // --- Number of discretization points along the x axis 
    const int NY = 256;   // --- Number of discretization points along the y axis 

    const int MAX_ITER = 1;  // --- Number of Jacobi iterations 

    // --- CPU temperature distributions 
    float *h_T    = (float *)calloc(NX * NY, sizeof(float)); 
    float *h_T_old   = (float *)calloc(NX * NY, sizeof(float)); 
    Initialize(h_T,  NX, NY); 
    Initialize(h_T_old, NX, NY); 
    float *h_T_GPU_result = (float *)malloc(NX * NY * sizeof(float)); 

    // --- GPU temperature distribution 
    float *d_T;  gpuErrchk(cudaMalloc((void**)&d_T,  NX * NY * sizeof(float))); 
    float *d_T_old; gpuErrchk(cudaMalloc((void**)&d_T_old, NX * NY * sizeof(float))); 

    gpuErrchk(cudaMemcpy(d_T,  h_T, NX * NY * sizeof(float), cudaMemcpyHostToDevice)); 
    gpuErrchk(cudaMemcpy(d_T_old, d_T, NX * NY * sizeof(float), cudaMemcpyDeviceToDevice)); 

    // --- Grid size 
    dim3 dimBlock(BLOCK_SIZE_X, BLOCK_SIZE_Y); 
    dim3 dimGrid (iDivUp(NX, BLOCK_SIZE_X), iDivUp(NY, BLOCK_SIZE_Y)); 

    // --- Jacobi iterations on the host 
    Jacobi_Iterator_CPU(h_T, h_T_old, NX, NY, MAX_ITER); 

    // --- Jacobi iterations on the device 
    for (int k=0; k<MAX_ITER; k=k+2) { 
     Jacobi_Iterator_GPU<<<dimGrid, dimBlock>>>(d_T,  d_T_old, NX, NY); // --- Update d_T_old  starting from data stored in d_T 
     Jacobi_Iterator_GPU<<<dimGrid, dimBlock>>>(d_T_old, d_T , NX, NY); // --- Update d_T   starting from data stored in d_T_old 
    } 

    // --- Copy result from device to host 
    gpuErrchk(cudaMemcpy(h_T_GPU_result, d_T, NX * NY * sizeof(float), cudaMemcpyDeviceToHost)); 

    // --- Calculate percentage root mean square error between host and device results 
    float sum = 0., sum_ref = 0.; 
    for (int j=0; j<NY; j++) 
     for (int i=0; i<NX; i++) { 
      sum  = sum  + (h_T_GPU_result[j * NX + i] - h_T[j * NX + i]) * (h_T_GPU_result[j * NX + i] - h_T[j * NX + i]); 
      sum_ref = sum_ref + h_T[j * NX + i]        * h_T[j * NX + i]; 
     } 
    printf("Percentage root mean square error = %f\n", 100.*sqrt(sum/sum_ref)); 

    // --- Release host memory 
    free(h_T); 
    free(h_T_GPU_result); 

    // --- Release device memory 
    gpuErrchk(cudaFree(d_T)); 
    gpuErrchk(cudaFree(d_T_old)); 

    return 0; 
} 

이러한 예제를 실행하는 데 필요한 Utilities.cuUtilities.cuh 파일

github page로 유지된다.