2014-04-14 3 views
0

매우 복잡한 CUDA 커널에 대해 동시 커널 시작을 구현하려고하므로 간단한 예제부터 시작하겠습니다. 단지 합계를 줄이는 커널을 시작합니다. 충분히 간단합니다. 동시 커널 시작 예제 - CUDA

#include <stdlib.h> 
#include <stdio.h> 
#include <time.h> 
#include <cuda.h> 

extern __shared__ char dsmem[]; 
__device__ double *scratch_space; 

__device__ double NDreduceSum(double *a, unsigned short length) 
{ 
    const int tid = threadIdx.x; 
    unsigned short k = length; 
    double *b; 

    b = scratch_space; 

    for (int i = tid; i < length; i+= blockDim.x) 
     b[i] = a[i]; 

    __syncthreads(); 

    do { 
     k = (k + 1)/2; 

     if (tid < k && tid + k < length) 
      b[tid] += b[tid + k]; 

     length = k; 
     __syncthreads(); 
    } while (k > 1); 

    return b[0]; 
} 

__device__ double reduceSum(double *a, unsigned short length) 
{ 
    const int tid = threadIdx.x; 
    unsigned short k = length; 

    do 
    { 
     k = (k + 1)/2; 

     if (tid < k && tid + k < length) 
      a[tid] += a[tid + k]; 

     length = k; 
     __syncthreads(); 
    } 
    while (k > 1); 

    return a[0]; 
} 

__global__ void kernel_thing(double *ad, int size) 
{ 
    double sum_1, sum_2, sum_3; 
    time_t begin, end, t1, t2, t3; 

    scratch_space = (double *) &dsmem[0]; 

    for (int j = 0; j < 1000000; j++) { 
     begin = clock(); 
     sum_1 = NDreduceSum(ad, size); 
     end = clock(); 
    } 

    __syncthreads(); 

    t1 = end - begin; 

    begin = clock(); 

    sum_2 = 0; 
    if (threadIdx.x == 0) { 
     for (int i = 0; i < size; i++) { 
      sum_2 += ad[i]; 
     } 
    } 

    __syncthreads(); 

    end = clock(); 

    t2 = end - begin; 

    __syncthreads(); 
    begin = clock(); 
    sum_3 = reduceSum(ad, size); 
    end = clock(); 

    __syncthreads(); 

    t3 = end - begin; 

    if (threadIdx.x == 0) { 
     printf("Sum found: %lf and %lf and %lf. In %ld and %ld and %ld ticks.\n", sum_1, sum_2, sum_3, t1, t2, t3); 
    } 
} 

int main(int argc, char **argv) 
{ 
    int i; 
    const int size = 512; 
    double *a, *ad, *b, *bd; 
    double sum_a, sum_b; 
    cudaStream_t stream_a, stream_b; 
    cudaError_t result; 
    cudaEvent_t a_start, a_stop, b_start, b_stop; 

    a = (double *) malloc(sizeof(double) * size); 
    b = (double *) malloc(sizeof(double) * size); 

    srand48(time(0)); 

    for (i = 0; i < size; i++) { 
     a[i] = drand48(); 
    } 

    for (i = 0; i < size; i++) { 
     b[i] = drand48(); 
    } 

    sum_a = 0; 
    for (i = 0; i < size; i++) { 
     sum_a += a[i]; 
    } 

    sum_b = 0; 
    for (i = 0; i < size; i++) { 
     sum_b += b[i]; 
    } 

    printf("Looking for sum_a %lf\n", sum_a); 
    printf("Looking for sum_b %lf\n", sum_b); 

    cudaEventCreate(&a_start); 
    cudaEventCreate(&b_start); 
    cudaEventCreate(&a_stop); 
    cudaEventCreate(&b_stop); 

    cudaMalloc((void **) &ad, sizeof(double) * size); 
    cudaMalloc((void **) &bd, sizeof(double) * size); 

    result = cudaStreamCreate(&stream_a); 
    result = cudaStreamCreate(&stream_b); 

    result = cudaMemcpyAsync(ad, a, sizeof(double) * size, cudaMemcpyHostToDevice, stream_a); 
    result = cudaMemcpyAsync(bd, b, sizeof(double) * size, cudaMemcpyHostToDevice, stream_b); 

    cudaEventRecord(a_start); 
    kernel_thing<<<1, 512, 49152, stream_a>>>(ad, size); 
    cudaEventRecord(a_stop); 
    cudaEventRecord(b_start); 
    kernel_thing<<<1, 512, 49152, stream_b>>>(bd, size); 
    cudaEventRecord(b_stop); 

    result = cudaMemcpyAsync(a, ad, sizeof(double) * size, cudaMemcpyDeviceToHost, stream_a); 
    result = cudaMemcpyAsync(b, bd, sizeof(double) * size, cudaMemcpyDeviceToHost, stream_b); 

    cudaEventSynchronize(a_stop); 
    cudaEventSynchronize(b_stop); 

    float a_ms = 0; 
    float b_ms = 0; 
    cudaEventElapsedTime(&a_ms, a_start, a_stop); 
    cudaEventElapsedTime(&b_ms, b_start, b_stop); 

    printf("%lf ms for A.\n", a_ms); 
    printf("%lf ms for B.\n", b_ms); 

    result = cudaStreamDestroy(stream_a); 
    result = cudaStreamDestroy(stream_b); 

    if (result != cudaSuccess) { 
     printf("I should probably do this after each important operation.\n"); 
    } 

    /* 
    printf("Matrix after:\n"); 
    for (i = 0; i < size; i++) { 
     printf("%lf ", a[i]); 
    } 
    printf("\n"); 
    */ 

    free(a); 
    free(b); 
    cudaFree(ad); 
    cudaFree(bd); 

    return 0; 
} 

은과 같이 컴파일 : 여기있다

CFLAGS = -arch sm_35 

CC = nvcc 

all: parallel 

parallel: parallel.cu 
    $(LINK.c) $^ -o [email protected] 

clean: 
    rm -f *.o core parallel 

나는 하나의 테슬라 K20X를 사용하고 있습니다. 당신이 볼 수있는 커널의 각 올바른 결과를 얻을 수와 내가 가진 무엇 인 약 4.5의 소요, 그래서

Looking for sum_a 247.983945 
Looking for sum_b 248.033749 
Sum found: 247.983945 and 247.983945 and 247.983945. In 3242 and 51600 and 4792 ticks. 
Sum found: 248.033749 and 248.033749 and 248.033749. In 3314 and 52000 and 4497 ticks. 
4645.079102 ms for A. 
4630.725098 ms for B. 
Application 577759 resources: utime ~8s, stime ~2s, Rss ~82764, inblocks ~406, outblocks ~967 

:이 간단한 예제를 실행하면

, 나는 다음과 같은 출력을 얻을 이전의 단일 커널 버전. 큰! 그러나 aprun 출력에서 ​​볼 수 있듯이 벽 시간은 실제로 약 10 초입니다. 이는 한 커널 버전보다 훨씬 큽니다. 따라서 커널이 병렬로 실행되지 않거나 동시에 커널을 시작할 때 예상했던 속도가 거의 향상되지 않는 것 같습니다.

TL으로, DR이 질문 ​​:

  1. 나는 내 코드 예제에서 아무것도 실종? 커널이 실제로 병렬로 실행되고 있습니까?
  2. Tesla K20X에서는 어떤 종류의 속도 향상이 필요합니까? 커널이 정확히 병렬로 실행되면서 같은 시간에 두 번 작업을 완료하면 안됩니까? 얼마나 많은 커널을 효율적으로 병렬로 실행할 수 있습니까?

도움 주셔서 감사합니다.

답변

1

커널 사이에있는 cudaEventRecord 작업으로 인해 직렬화가 발생합니다.

지금 결과는 점점 :

4645.079102 ms for A. 
4630.725098 ms for B. 

인해이 직렬화 백투백 있습니다. 대신

, 단지 시간 전체 커널 발사 순서 :

cudaEventRecord(a_start); 
kernel_thing<<<1, 512, 49152, stream_a>>>(ad, size); 
kernel_thing<<<1, 512, 49152, stream_b>>>(bd, size); 
cudaEventRecord(a_stop); 

그리고 난 당신이 이전 커널의 하나로서 거의 동일 (a_start, a_stop)에 대한 경과 시간을 볼 생각 (~ 4600ms)을 나타내는 더 많거나 덜 완전한 동시성. CUDA 6 RC를 사용하여 커널에서 printf보다는 호스트에 데이터를 복사하고 커널 호출간에 cudaEventRecord 작업을 제거했습니다. 전체 실행 시간이 ~ 4.8 초입니다. 이와 같은 테스트를 실행할 때 커널에서 printf를 사용하지 않을

  • : 나는 cudaEventRecord 배열을 수정하지 않은 경우, 대신 내 실행 시간 ~ 8.3s

    다른 몇 가지 메모를했다.

  • 호스트 버퍼를 malloc으로 할당하면 계산이 겹치지 않고 cudaMemcpyAsync이됩니다. cudaHostAlloc을 사용해야합니다.
  • 나는 먼저 concurrent kernels cuda sample을 달리고 이해하는 것으로 시작할 것입니다.
  • programming guide
의 해당 섹션을 검토하는 것이 좋습니다.