2016-02-11 3 views
0

Я действительно не знаю, как назвать проблему, с которой я столкнулся, поэтому любой мод переименует ее соответственно, если вы так считаете.Проблема производительности CUDA

я получил следующую матрицу векторного умножения ядра:

__global__ void dmv_gpu_shmem(const value_t *a, const value_t *x, value_t *y, 
           size_t n) 
{ 
    extern __shared__ value_t shmem_buf[]; 
    int ltid = threadIdx.x; 
    int gtid = get_global_tid(); 
    value_t _y = 0.0; 

    if (gtid > n) 
     return; 

    int last_id = n/blockDim.x; 

    for(size_t j=0; j< last_id; j++) { 

     shmem_buf[ltid] = x[blockDim.x*j + ltid]; 
     __syncthreads(); 

     for(size_t i=0; i< blockDim.x; i++) { 
      _y += a[gtid + (i + j*blockDim.x)*n] * shmem_buf[i]; 
     } 
     __syncthreads(); 
    } 

    y[gtid] = _y; 

} 

Я должен проверить это ядро ​​в течение некоторого размера блока, но результаты синхронизации я получил где не так хорошо. Поэтому я решил изменить это следующим образом и зафиксировать размер блока 32 при вызове ядра

__global__ void dmv_gpu_shmem(const value_t *a, const value_t *x, value_t *y, 
           size_t n) 
{ 
    extern __shared__ value_t shmem_buf[]; 
    int ltid = threadIdx.x; 
    int gtid = get_global_tid(); 
    value_t _y = 0.0; 

    if (gtid > n) 
     return; 

    int last_id = n/32; 

    for(size_t j=0; j< last_id; j++) { 

     shmem_buf[ltid] = x[32*j + ltid]; 
     __syncthreads(); 

     for(size_t i=0; i< 32; i++) { 
      _y += a[gtid + (i + j*32)*n] * shmem_buf[i]; 
     } 
     __syncthreads(); 
    } 

    y[gtid] = _y; 

} 

К моему большому удивлению, ядро ​​не получил несколько раз лучше с точки зрения времени выполнения, и я совершенно не знаю, почему это происходит.

Может ли кто-нибудь более опытный объяснить это?

Также, учитывая эту ситуацию, как я могу получить максимум из моего ядра, используя все различные блоки, которые я хочу? Я не могу этого сделать для всех ...

размера блока

Edit:

Это должен быть рабочим репро случай:

#include <stdlib.h> 
#include <stdio.h> 
#include <sys/time.h> 
#include <cuda.h> 
#include "cublas_v2.h" //CUBLAS LIBRARY 

#ifndef VALUES_MAX 
# define VALUES_MAX 1. 
#endif 

#ifndef EPS 
# define EPS 1.e-6 
#endif 

#ifndef NR_ITER 
# define NR_ITER 200 
#endif 

enum 
{ 
    GPU_NAIVE = 0, 
    GPU_COALESCED, 
    GPU_SHMEM, 
    GPU_KERNEL_END 
}; 

void *gpu_alloc(size_t count) 
{ 
    void *ret; 
    if (cudaMalloc(&ret, count) != cudaSuccess) { 
     ret = NULL; 
    } 

    return ret; 
} 

int copy_to_gpu(const void *host, void *gpu, size_t count) 
{ 
    if (cudaMemcpy(gpu, host, count, cudaMemcpyHostToDevice) != cudaSuccess) 
     return -1; 
    return 0; 
} 

int copy_from_gpu(void *host, const void *gpu, size_t count) 
{ 
    if (cudaMemcpy(host, gpu, count, cudaMemcpyDeviceToHost) != cudaSuccess) 
     return -1; 
    return 0; 
} 

void mat_init_rand(float **a, size_t n, float max) 
{ 
    size_t i, j; 
    for (i = 0; i < n; ++i) 
     { 
      for (j = 0; j < n; ++j) 
       { 
        //printf("%d %d \n", i, j); 
        a[i][j] = 2 * (((float) drand48()) - 0.5) * max; 
       } 
     } 
} 

void vec_init(float *v, size_t n, float val) 
{ 
    size_t i; 
    for (i = 0; i < n; ++i) 
     { 
      v[i] = val; 
     } 
} 

void vec_init_rand(float *v, size_t n, float max) 
{ 
    size_t i; 
    for (i = 0; i < n; ++i) 
     { 
      v[i] = 2 * (((float) drand48()) - 0.5) * max; 
     } 
} 

void vec_print(const float *v, size_t n) 
{ 
    size_t i; 
    for (i = 0; i < n; ++i) 
     printf("%f \n", v[i]); 
} 


void **calloc_2d(size_t n, size_t m, size_t size) 
{ 
    char **ret = (char **) malloc(n*sizeof(char *)); 
    if (ret) { 
     char *area = (char *) calloc(n*m, size); 
     if (area) { 
      for (size_t i = 0; i < n; ++i) 
       ret[i] = (char *) &area[i*m*size]; 
     } else { 
      free(ret); 
      ret = NULL; 
     } 
    } 

    return (void **) ret; 
} 

void **copy_2d(void **dst, const void **src, size_t n, size_t m, size_t size) 
{ 
    memcpy(dst[0], src[0], n*m*size); 
    return dst; 
} 

void free_2d(void **array) 
{ 
    free(array[0]); 
    free(array); 
} 

__global__ void dmv_gpu_shmem(const float *a, const float *x, float *y, 
           size_t n) 
{ 
    extern __shared__ float shmem_buf[]; 
    int ltid = threadIdx.x; 
    int gtid = blockIdx.x*blockDim.x+threadIdx.x; 
    float _y = 0.0; 

    if (gtid > n) 
     return; 

    int last_id = n/blockDim.x; 

    for(size_t j=0; j< last_id; j++) { 

     shmem_buf[ltid] = x[blockDim.x*j + ltid]; 
     __syncthreads(); 

     for(size_t i=0; i< blockDim.x; i++) { 
      _y += a[gtid + (i + j*blockDim.x)*n] * shmem_buf[i]; 
     } 
     __syncthreads(); 
    } 

    y[gtid] = _y; 

} 

__global__ void dmv_gpu_shmem_static(const float *a, const float *x, float *y, 
           size_t n) 
{ 
    extern __shared__ float shmem_buf[]; 
    int ltid = threadIdx.x; 
    int gtid = blockIdx.x*blockDim.x+threadIdx.x; 
    float _y = 0.0; 

    if (gtid > n) 
     return; 

    int last_id = n/32; 

    for(size_t j=0; j< last_id; j++) { 

     shmem_buf[ltid] = x[32*j + ltid]; 
     __syncthreads(); 

     for(size_t i=0; i< 32; i++) { 
      _y += a[gtid + (i + j*32)*n] * shmem_buf[i]; 
     } 
     __syncthreads(); 
    } 

    y[gtid] = _y; 

} 

int main(int argc, char **argv) 
{ 
    if (argc < 2) { 
     printf("Wrong arguments \n"); 
     return -1; 

    } 

    size_t orig_n = atoi(argv[1]); 

    /* Read block size and kernel to launch from the environment */ 
    const char *env_gpu_kernel = getenv("GPU_KERNEL"); 
    const char *env_gpu_block_size = getenv("GPU_BLOCK_SIZE"); 
    int kernel = (env_gpu_kernel) ? atoi(env_gpu_kernel) : GPU_NAIVE; 
    int block_size = (env_gpu_block_size) ? atoi(env_gpu_block_size) : 256; 

    //Adjust Matrix to fit blocksize 
    size_t n = ((orig_n - 1)/block_size + 1)*block_size; 
    int grid_size = (n-1)/block_size + 1; 

    printf("Matrix size: %zd\n", orig_n); 
    printf("Input Block size: %zd\n", block_size); 
    printf("Adjusted matrix size: %zd\n", n); 

    /* 
    * Allocate the structures. 
    * 
    * Initialization to zero is crucial if you adjusted the matrix 
    * size. 
    */ 
    float **A = (float **) calloc_2d(n, n, sizeof(**A)); 
    float *x = (float *) calloc(n, sizeof(*x)); 
    float *y = (float *) calloc(n, sizeof(*y)); 

    /* Initialize */ 
    srand48(0); 
    mat_init_rand(A, orig_n, VALUES_MAX); 
    vec_init_rand(x, orig_n, VALUES_MAX); 

    vec_init(y, orig_n, 0.0); 


    printf("Setup Complete\n"); 

    /* 
    * FILLME: Set up the blocks, grid and shared memory depending on 
    *   the kernel. Make any transformations to the input 
    *   matrix here. 
    */ 

    //Transposing Matrix for Shared and Coalesced Matrices 
    float tmp; 
    for(size_t i=0;i<n;i++) 
     for(size_t j=i+1;j<n;j++) { 

      tmp=A[i][j]; 
      A[i][j] = A[j][i]; 
      A[j][i] = tmp; 
     } 

    dim3 gpu_block(block_size, 1); // Number of threads 
    dim3 gpu_grid(grid_size, 1); // Number of blocks 
    size_t shmem_size = 0;   // Shared memory size 
    /* Set SHARED MEMORY size */ 
    shmem_size = block_size * sizeof(float); 

    printf(">>>> Begin of record <<<<\n"); 
    printf("Block size: %dx%d\n", gpu_block.x, gpu_block.y); 
    printf("Grid size : %dx%d\n", gpu_grid.x, gpu_grid.y); 
    printf("Shared memory size: %ld bytes\n", shmem_size); 

    /* GPU allocations */ 
    float *gpu_A = (float *) gpu_alloc(n*n*sizeof(*gpu_A)); 
    float *gpu_x = (float *) gpu_alloc(n*sizeof(*gpu_x)); 
    float *gpu_y = (float *) gpu_alloc(n*sizeof(*gpu_y)); 

    /* Copy data to GPU */ 
    copy_to_gpu(A[0], gpu_A, n*n*sizeof(*gpu_A)); 
    copy_to_gpu(x, gpu_x, n*sizeof(*gpu_x)); 

    /* Reset y and copy it to GPU */ 
    vec_init(y, n, 0.0); 
    copy_to_gpu(y, gpu_y, n*sizeof(*gpu_y)); 


    dmv_gpu_shmem<<<gpu_grid,gpu_block,shmem_size>>> 
     (gpu_A, gpu_x, gpu_y, n); 

    if (cudaGetLastError() != cudaSuccess) 
     printf("gpu kernel failed to launch \n"); 

    dmv_gpu_shmem_static<<<gpu_grid,gpu_block,shmem_size>>> 
     (gpu_A, gpu_x, gpu_y, n); 

    if (cudaGetLastError() != cudaSuccess) 
     printf("gpu kernel failed to launch \n"); 


    cudaDeviceSynchronize(); 

    /* Free resources on host */ 
    free_2d((void **) A); 
    free(x); 
    free(y); 

    /* Free resources on GPU */ 
    cudaFree(gpu_A); 
    cudaFree(gpu_x); 
    cudaFree(gpu_y); 

    return EXIT_SUCCESS; 
} 

Compile с

nvcc dmv_test_case.cu 

Выполнить с

GPU_KERNEL=2 GPU_BLOCK_SIZE=32 ./a.out 2048 

G Переменная PU_KERNEL в этом случае ничего не делает. GPU_BLOCK_SIZE очевиден. Число аргумент размер вектора (п) и размер матрицы (пхп)

+0

Возможно компилятор оптимизации из-за подсчета фиксированного цикла. Тем не менее, будет полезен полный пример воспроизведения. – talonmies

+0

Не nvcc делает автоматическую оптимизацию на ядрах, я думаю, что прочитал это в руководстве по программированию. Я попытаюсь сделать репродуцирование, но это слишком сложно, так как вся программа снова разбита на 5 файлов ... –

+0

Да, но петли могут быть развернуты во втором ядре, а не в первом из-за постоянного подсчета поездок , – talonmies

ответ

1

Может кто-то более опытный это объяснить?

Полный анализ выходит за рамки того, что я готов предложить, но я начну с вами по пути. Как указано в @talonmies, это, по крайней мере, частично связано с «оптимизацией компилятора из-за фиксированного числа отключений».

Когда я запускаю код с nvprof --print-gpu-trace ..., я наблюдаю за 3-кратной разницей в времени выполнения ядра между двумя ядрами (на устройстве cc2.0). В этом может быть некоторый перекос, потому что мы называем «более быстрое» ядро ​​после «медленного» ядра, но оно работает с одними и теми же данными, поэтому может быть некоторое преимущество кэширования за счет второго. Но давайте проигнорируем это. Давайте посмотрим на вывод кода SASS из cuobjdump -sass для вашего кода:

медленнее ядра:

 Function : _Z13dmv_gpu_shmemPKfS0_Pfm 
.headerflags @"EF_CUDA_SM20 EF_CUDA_PTX_SM(EF_CUDA_SM20)" 
/*0000*/   MOV R1, c[0x1][0x100];       /* 0x2800440400005de4 */ 
.... 
.... 
/*01f0*/   LD.E R18, [R2];         /* 0x8400000000249c85 */ 
/*01f8*/   IADD R19.CC, R19, 0x1;       /* 0x4801c0000534dc03 */ 
/*0200*/   LDS R17, [R21];         /* 0xc100000001545c85 */ 
/*0208*/   IADD.X R20, R20, RZ;        /* 0x48000000fd451c43 */ 
/*0210*/   ISUB RZ.CC, R19, c[0x0][0x8];      /* 0x48014000213fdd03 */ 
/*0218*/   IADD R21, R21, 0x4;        /* 0x4800c00011555c03 */ 
/*0220*/   ISETP.LT.U32.X.AND P0, PT, R20, RZ, PT;   /* 0x188e0000fd41dc43 */ 
/*0228*/   IADD R2.CC, R2, R15;        /* 0x480100003c209c03 */ 
/*0230*/   IADD.X R3, R3, R16;        /* 0x480000004030dc43 */ 
/*0238*/   FFMA R6, R18, R17, R6;       /* 0x300c000045219c00 */ 
/*0240*/  @P0 BRA 0x1f0;          /* 0x4003fffea00001e7 */ 

быстрее «статические» ядро:

 Function : _Z20dmv_gpu_shmem_staticPKfS0_Pfm 
.headerflags @"EF_CUDA_SM20 EF_CUDA_PTX_SM(EF_CUDA_SM20)" 
/*0000*/   MOV R1, c[0x1][0x100];       /* 0x2800440400005de4 */ 
.... 
.... 
/*0110*/   LD.E R10, [R2];         /* 0x8400000000229c85 */ 
/*0118*/   STS [R6], R10;         /* 0xc900000000629c85 */ 
/*0120*/   BAR.RED.POPC RZ, RZ, RZ, PT;      /* 0x50ee0000ffffdc04 */ 
/*0128*/   LD.E R22, [R8];         /* 0x8400000000859c85 */ 
/*0130*/   IADD R10.CC, R8, R14;        /* 0x4801000038829c03 */ 
/*0138*/   IADD.X R11, R9, R15;        /* 0x480000003c92dc43 */ 
/*0140*/   IADD R18.CC, R10, R14;       /* 0x4801000038a49c03 */ 
/*0148*/   LD.E R21, [R10];         /* 0x8400000000a55c85 */ 
/*0150*/   IADD.X R19, R11, R15;        /* 0x480000003cb4dc43 */ 
/*0158*/   IADD R16.CC, R18, R14;       /* 0x4801000039241c03 */ 
/*0160*/   LD.E R24, [R18];         /* 0x8400000001261c85 */ 
/*0168*/   IADD.X R17, R19, R15;        /* 0x480000003d345c43 */ 
/*0170*/   LDS.128 R8, [RZ];         /* 0xc100000003f21cc5 */ 
/*0178*/   LD.E R25, [R16];         /* 0x8400000001065c85 */ 
/*0180*/   IADD R16.CC, R16, R14;       /* 0x4801000039041c03 */ 
/*0188*/   IADD.X R17, R17, R15;        /* 0x480000003d145c43 */ 
/*0190*/   IADD R18.CC, R16, R14;       /* 0x4801000039049c03 */ 
/*0198*/   IADD.X R19, R17, R15;        /* 0x480000003d14dc43 */ 
/*01a0*/   LD.E R23, [R18];         /* 0x840000000125dc85 */ 
/*01a8*/   FFMA R8, R22, R8, R20;       /* 0x3028000021621c00 */ 
/*01b0*/   LD.E R22, [R16];         /* 0x8400000001059c85 */ 
/*01b8*/   IADD R20.CC, R18, R14;       /* 0x4801000039251c03 */ 
/*01c0*/   FFMA R8, R21, R9, R8;        /* 0x3010000025521c00 */ 
/*01c8*/   IADD.X R21, R19, R15;        /* 0x480000003d355c43 */ 
/*01d0*/   IADD R16.CC, R20, R14;       /* 0x4801000039441c03 */ 
/*01d8*/   FFMA R8, R24, R10, R8;       /* 0x3010000029821c00 */ 
/*01e0*/   LD.E R24, [R20];         /* 0x8400000001461c85 */ 
/*01e8*/   IADD.X R17, R21, R15;        /* 0x480000003d545c43 */ 
/*01f0*/   FFMA R26, R25, R11, R8;       /* 0x301000002d969c00 */ 
/*01f8*/   LD.E R25, [R16];         /* 0x8400000001065c85 */ 
/*0200*/   LDS.128 R8, [0x10];        /* 0xc100000043f21cc5 */ 
/*0208*/   IADD R16.CC, R16, R14;       /* 0x4801000039041c03 */ 
/*0210*/   IADD.X R17, R17, R15;        /* 0x480000003d145c43 */ 
/*0218*/   IADD R18.CC, R16, R14;       /* 0x4801000039049c03 */ 
/*0220*/   IADD.X R19, R17, R15;        /* 0x480000003d14dc43 */ 
/*0228*/   IADD R20.CC, R18, R14;       /* 0x4801000039251c03 */ 
/*0230*/   IADD.X R21, R19, R15;        /* 0x480000003d355c43 */ 
/*0238*/   FFMA R26, R22, R8, R26;       /* 0x3034000021669c00 */ 
/*0240*/   LD.E R22, [R16];         /* 0x8400000001059c85 */ 
/*0248*/   FFMA R8, R23, R9, R26;       /* 0x3034000025721c00 */ 
/*0250*/   LD.E R23, [R18];         /* 0x840000000125dc85 */ 
/*0258*/   IADD R16.CC, R20, R14;       /* 0x4801000039441c03 */ 
/*0260*/   IADD.X R17, R21, R15;        /* 0x480000003d545c43 */ 
/*0268*/   FFMA R8, R24, R10, R8;       /* 0x3010000029821c00 */ 
/*0270*/   LD.E R24, [R20];         /* 0x8400000001461c85 */ 
/*0278*/   FFMA R26, R25, R11, R8;       /* 0x301000002d969c00 */ 
/*0280*/   LD.E R25, [R16];         /* 0x8400000001065c85 */ 
/*0288*/   LDS.128 R8, [0x20];        /* 0xc100000083f21cc5 */ 
/*0290*/   IADD R16.CC, R16, R14;       /* 0x4801000039041c03 */ 
/*0298*/   IADD.X R17, R17, R15;        /* 0x480000003d145c43 */ 
/*02a0*/   IADD R18.CC, R16, R14;       /* 0x4801000039049c03 */ 
/*02a8*/   IADD.X R19, R17, R15;        /* 0x480000003d14dc43 */ 
/*02b0*/   IADD R20.CC, R18, R14;       /* 0x4801000039251c03 */ 
/*02b8*/   IADD.X R21, R19, R15;        /* 0x480000003d355c43 */ 
/*02c0*/   FFMA R26, R22, R8, R26;       /* 0x3034000021669c00 */ 
/*02c8*/   LD.E R22, [R16];         /* 0x8400000001059c85 */ 
/*02d0*/   FFMA R8, R23, R9, R26;       /* 0x3034000025721c00 */ 
/*02d8*/   LD.E R23, [R18];         /* 0x840000000125dc85 */ 
/*02e0*/   IADD R16.CC, R20, R14;       /* 0x4801000039441c03 */ 
/*02e8*/   IADD.X R17, R21, R15;        /* 0x480000003d545c43 */ 
/*02f0*/   FFMA R8, R24, R10, R8;       /* 0x3010000029821c00 */ 
/*02f8*/   LD.E R24, [R20];         /* 0x8400000001461c85 */ 
/*0300*/   FFMA R26, R25, R11, R8;       /* 0x301000002d969c00 */ 
/*0308*/   LD.E R25, [R16];         /* 0x8400000001065c85 */ 
/*0310*/   LDS.128 R8, [0x30];        /* 0xc1000000c3f21cc5 */ 
/*0318*/   IADD R16.CC, R16, R14;       /* 0x4801000039041c03 */ 
/*0320*/   IADD.X R17, R17, R15;        /* 0x480000003d145c43 */ 
/*0328*/   IADD R18.CC, R16, R14;       /* 0x4801000039049c03 */ 
/*0330*/   IADD.X R19, R17, R15;        /* 0x480000003d14dc43 */ 
/*0338*/   IADD R20.CC, R18, R14;       /* 0x4801000039251c03 */ 
/*0340*/   IADD.X R21, R19, R15;        /* 0x480000003d355c43 */ 
/*0348*/   FFMA R26, R22, R8, R26;       /* 0x3034000021669c00 */ 
/*0350*/   LD.E R22, [R16];         /* 0x8400000001059c85 */ 
/*0358*/   FFMA R8, R23, R9, R26;       /* 0x3034000025721c00 */ 
/*0360*/   LD.E R23, [R18];         /* 0x840000000125dc85 */ 
/*0368*/   IADD R16.CC, R20, R14;       /* 0x4801000039441c03 */ 
/*0370*/   IADD.X R17, R21, R15;        /* 0x480000003d545c43 */ 
/*0378*/   FFMA R8, R24, R10, R8;       /* 0x3010000029821c00 */ 
/*0380*/   LD.E R24, [R20];         /* 0x8400000001461c85 */ 
/*0388*/   FFMA R26, R25, R11, R8;       /* 0x301000002d969c00 */ 
/*0390*/   LD.E R25, [R16];         /* 0x8400000001065c85 */ 
/*0398*/   LDS.128 R8, [0x40];        /* 0xc100000103f21cc5 */ 
/*03a0*/   IADD R16.CC, R16, R14;       /* 0x4801000039041c03 */ 
/*03a8*/   IADD.X R17, R17, R15;        /* 0x480000003d145c43 */ 
/*03b0*/   IADD R18.CC, R16, R14;       /* 0x4801000039049c03 */ 
/*03b8*/   IADD.X R19, R17, R15;        /* 0x480000003d14dc43 */ 
/*03c0*/   IADD R20.CC, R18, R14;       /* 0x4801000039251c03 */ 
/*03c8*/   IADD.X R21, R19, R15;        /* 0x480000003d355c43 */ 
/*03d0*/   FFMA R26, R22, R8, R26;       /* 0x3034000021669c00 */ 
/*03d8*/   LD.E R22, [R16];         /* 0x8400000001059c85 */ 
/*03e0*/   FFMA R8, R23, R9, R26;       /* 0x3034000025721c00 */ 
/*03e8*/   LD.E R23, [R18];         /* 0x840000000125dc85 */ 
/*03f0*/   IADD R16.CC, R20, R14;       /* 0x4801000039441c03 */ 
/*03f8*/   LD.E R20, [R20];         /* 0x8400000001451c85 */ 
/*0400*/   IADD.X R17, R21, R15;        /* 0x480000003d545c43 */ 
/*0408*/   FFMA R8, R24, R10, R8;       /* 0x3010000029821c00 */ 
/*0410*/   FFMA R24, R25, R11, R8;       /* 0x301000002d961c00 */ 
/*0418*/   LD.E R25, [R16];         /* 0x8400000001065c85 */ 
/*0420*/   LDS.128 R8, [0x50];        /* 0xc100000143f21cc5 */ 
/*0428*/   IADD R16.CC, R16, R14;       /* 0x4801000039041c03 */ 
/*0430*/   IADD.X R17, R17, R15;        /* 0x480000003d145c43 */ 
/*0438*/   IADD R18.CC, R16, R14;       /* 0x4801000039049c03 */ 
/*0440*/   LD.E R21, [R16];         /* 0x8400000001055c85 */ 
/*0448*/   IADD.X R19, R17, R15;        /* 0x480000003d14dc43 */ 
/*0450*/   IADD R16.CC, R18, R14;       /* 0x4801000039241c03 */ 
/*0458*/   IADD.X R17, R19, R15;        /* 0x480000003d345c43 */ 
/*0460*/   FFMA R8, R22, R8, R24;       /* 0x3030000021621c00 */ 
/*0468*/   LD.E R24, [R18];         /* 0x8400000001261c85 */ 
/*0470*/   FFMA R8, R23, R9, R8;        /* 0x3010000025721c00 */ 
/*0478*/   IADD R18.CC, R16, R14;       /* 0x4801000039049c03 */ 
/*0480*/   FFMA R8, R20, R10, R8;       /* 0x3010000029421c00 */ 
/*0488*/   IADD.X R19, R17, R15;        /* 0x480000003d14dc43 */ 
/*0490*/   IADD R20.CC, R18, R14;       /* 0x4801000039251c03 */ 
/*0498*/   LD.E R18, [R18];         /* 0x8400000001249c85 */ 
/*04a0*/   FFMA R22, R25, R11, R8;       /* 0x301000002d959c00 */ 
/*04a8*/   LDS.128 R8, [0x60];        /* 0xc100000183f21cc5 */ 
/*04b0*/   LD.E R25, [R16];         /* 0x8400000001065c85 */ 
/*04b8*/   FFMA R16, R21, R8, R22;       /* 0x302c000021541c00 */ 
/*04c0*/   IADD.X R21, R19, R15;        /* 0x480000003d355c43 */ 
/*04c8*/   IADD R22.CC, R20, R14;       /* 0x4801000039459c03 */ 
/*04d0*/   LD.E R20, [R20];         /* 0x8400000001451c85 */ 
/*04d8*/   IADD.X R23, R21, R15;        /* 0x480000003d55dc43 */ 
/*04e0*/   IADD R8.CC, R22, R14;        /* 0x4801000039621c03 */ 
/*04e8*/   LD.E R22, [R22];         /* 0x8400000001659c85 */ 
/*04f0*/   FFMA R24, R24, R9, R16;       /* 0x3020000025861c00 */ 
/*04f8*/   IADD.X R9, R23, R15;        /* 0x480000003d725c43 */ 
/*0500*/   IADD R16.CC, R8, R14;        /* 0x4801000038841c03 */ 
/*0508*/   LD.E R19, [R8];         /* 0x840000000084dc85 */ 
/*0510*/   IADD.X R17, R9, R15;        /* 0x480000003c945c43 */ 
/*0518*/   LD.E R21, [R16];         /* 0x8400000001055c85 */ 
/*0520*/   FFMA R24, R25, R10, R24;       /* 0x3030000029961c00 */ 
/*0528*/   FFMA R18, R18, R11, R24;       /* 0x303000002d249c00 */ 
/*0530*/   LDS.128 R8, [0x70];        /* 0xc1000001c3f21cc5 */ 
/*0538*/   FFMA R18, R20, R8, R18;       /* 0x3024000021449c00 */ 
/*0540*/   IADD R8.CC, R16, R14;        /* 0x4801000039021c03 */ 
/*0548*/   FFMA R9, R22, R9, R18;       /* 0x3024000025625c00 */ 
/*0550*/   FFMA R10, R19, R10, R9;       /* 0x3012000029329c00 */ 
/*0558*/   IADD.X R9, R17, R15;        /* 0x480000003d125c43 */ 
/*0560*/   FFMA R20, R21, R11, R10;       /* 0x301400002d551c00 */ 
/*0568*/   BAR.RED.POPC RZ, RZ, RZ, PT;      /* 0x50ee0000ffffdc04 */ 
/*0570*/   IADD R7.CC, R7, 0x1;        /* 0x4801c0000471dc03 */ 
/*0578*/   IADD.X R13, R13, RZ;        /* 0x48000000fcd35c43 */ 
/*0580*/   ISUB RZ.CC, R7, R4;        /* 0x48010000107fdd03 */ 
/*0588*/   ISETP.LT.U32.X.AND P0, PT, R13, R5, PT;   /* 0x188e000014d1dc43 */ 
/*0590*/   IADD R2.CC, R2, 0x80;        /* 0x4801c00200209c03 */ 
/*0598*/   IADD.X R3, R3, RZ;        /* 0x48000000fc30dc43 */ 
/*05a0*/  @P0 BRA 0x110;          /* 0x4003ffeda00001e7 */ 

Благодаря SO пределах количества символов, я «нужно было просто извлечь из каждого ядра« основной цикл », который выполняет фактическую матричную векторную умноженную арифметику. Это выполняется инструкциями FFMA - с плавающей запятой многократно добавьте. Если вы посмотрите на матрицу-векторную умноженную арифметику, вы увидите, что это последовательность операций с множественным добавлением.

Сравнивая выше 2 случая, мы можем сделать несколько замечаний:

  1. Чем медленнее ядро ​​имеет только одну FFMA инструкции во всем ядре - и это в цикле я показал. Чтобы выполнить 32 операции умножения, цикл должен выполняться 32 раза. У более быстрого ядра было 32 отдельных команды FFMA. Это то, что называется «разворачивается». Петля из предыдущего ядра исчезла. Поэтому последовательность команд нужно выполнить только один раз, чтобы выполнить все 32 необходимые операции умножения.

  2. В соответствии с разверткой мы видим, что более медленный («свернутый») код содержит около 11 инструкций в цикле. Развернутый код имеет ~ 150 инструкций.

  3. Поскольку свернутый код должен выполняться 32 раза, это должно быть выполнено как 32x11 или около 350 инструкций. Сравнивая это с разворачиваемым случаем, мы видим, что должно выполняться только половина всех инструкций.

Это может быть ручное объяснение, по меньшей мере, для разницы в производительности между двумя случаями. Поскольку развернутый цикл дает компилятору лучшую возможность комбинировать промежуточные шаги, он может уменьшить общий счетчик команд, оптимизируя более крупные разделы кода, чем то, что дает цикл. Вероятно, есть и некоторые преимущества из-за того, что развернутый код не требует никакого разветвления во время выполнения инструкций 32 FFMA.

Также, учитывая эту ситуацию, как я могу получить максимум из моего ядра, используя все различные блоки, которые я хочу? Я не могу сделать эту вещь для всех блоков ...

Ну, сколько размеров блоков, действительно, имеет настоящий интерес? Обычные рекомендации cuda включают размеры блоков, кратные 32 или даже двоичной степени 2, которые «не слишком малы» и «не слишком велики». Для многократной векторной матрицы реального мира вам может потребоваться только несколько размеров блоков, таких как 64, 128, 256 и 512. Вы можете просто написать их вручную, но templating может быть другим подходом на самом деле много гибкости здесь, для этой конкретной замены, о которой вы беспокоитесь. Что-то вроде этого:

template <int BS> 
__global__ void dmv_gpu_shmem_templ(const float *a, const float *x, float *y, 
           size_t n) 
{ 
    extern __shared__ float shmem_buf[]; 
    int ltid = threadIdx.x; 
    int gtid = blockIdx.x*blockDim.x+threadIdx.x; 
    float _y = 0.0; 

    if (gtid > n) 
     return; 

    int last_id = n/BS; 

    for(size_t j=0; j< last_id; j++) { 

     shmem_buf[ltid] = x[BS*j + ltid]; 
     __syncthreads(); 

     for(size_t i=0; i< BS; i++) { 
      _y += a[gtid + (i + j*BS)*n] * shmem_buf[i]; 
     } 
     __syncthreads(); 
    } 

    y[gtid] = _y; 

} 

и:

if(gpu_block == 32) 
    dmv_gpu_shmem_templ<32><<<gpu_grid,gpu_block,shmem_size>>> 
    (gpu_A, gpu_x, gpu_y, n); 
+0

До тех пор, пока вы не ответите, я был в курсе того же направления, но ваш ответ действительно объясняет каждую деталь, которую я отсутствовал. Большое спасибо. –

Смежные вопросы