Где находится граница между началом и окончанием запуска процессора и запуска графического процессора Nvidia Profiling NVPROF? - PullRequest
1 голос
/ 15 мая 2019

Каково определение начала и конца запуска ядра в CPU и GPU (желтый блок)?Где граница между ними?

Обратите внимание, что начало, конец и продолжительность этих желтых блоков в CPU и GPU различаются. Почему вызов CPU vecAdd<<<gridSize, blockSize>>>(d_a, d_b, d_c, n); занимает столько времени?

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// CUDA kernel. Each thread takes care of one element of c
__global__ void vecAdd(double *a, double *b, double *c, int n)
{
    // Get our global thread ID
    int id = blockIdx.x*blockDim.x+threadIdx.x;
    //printf("id = %d \n", id);

    // Make sure we do not go out of bounds
    if (id < n)
        c[id] = a[id] + b[id];
}

int main( int argc, char* argv[] )
{
    // Size of vectors
    int n = 1000000;

    // Host input vectors
    double *h_a;
    double *h_b;
    //Host output vector
    double *h_c;

    // Device input vectors
    double *d_a;
    double *d_b;
    //Device output vector
    double *d_c;

    // Size, in bytes, of each vector
    size_t bytes = n*sizeof(double);

    // Allocate memory for each vector on host
    h_a = (double*)malloc(bytes);
    h_b = (double*)malloc(bytes);
    h_c = (double*)malloc(bytes);

    // Allocate memory for each vector on GPU
    cudaMalloc(&d_a, bytes);
    cudaMalloc(&d_b, bytes);
    cudaMalloc(&d_c, bytes);

    int i;
    // Initialize vectors on host
    for( i = 0; i < n; i++ ) {
        h_a[i] = sin(i)*sin(i);
        h_b[i] = cos(i)*cos(i);
    }

    // Copy host vectors to device
    cudaMemcpy( d_a, h_a, bytes, cudaMemcpyHostToDevice);
    cudaMemcpy( d_b, h_b, bytes, cudaMemcpyHostToDevice);

    int blockSize, gridSize;

    // Number of threads in each thread block
    blockSize = 1024;

    // Number of thread blocks in grid
    gridSize = (int)ceil((float)n/blockSize);

    // Execute the kernel
    vecAdd<<<gridSize, blockSize>>>(d_a, d_b, d_c, n);

    // Copy array back to host
    cudaMemcpy( h_c, d_c, bytes, cudaMemcpyDeviceToHost );

    // Sum up vector c and print result divided by n, this should equal 1 within error
    double sum = 0;
    for(i=0; i<n; i++)
        sum += h_c[i];
    printf("final result: %f\n", sum/n);

    // Release device memory
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);

    // Release host memory
    free(h_a);
    free(h_b);
    free(h_c);

    return 0;
}

ЦП желтый блок:

enter image description here

Графический блок желтый:

enter image description here

1 Ответ

2 голосов
/ 15 мая 2019

Обратите внимание, что вы упоминаете NVPROF, но изображения, которые вы показываете, взяты из nvvp - визуального профилировщика. nvprof - это профилировщик командной строки

Запуски ядра GPU являются асинхронными. Это означает, что поток ЦП запускает ядро, но не ожидает его завершения. Фактически, процессорная нагрузка фактически помещает ядро ​​в очередь запуска - фактическое выполнение ядра может быть отложено, если что-то еще происходит в GPU.

Таким образом, не существует определенной связи между активностью CPU (API) и активностью GPU по времени, за исключением того, что запуск ядра CPU должен явно предшествовать (хотя бы немного) выполнению ядра GPU.

Желтый блок ЦП (API) представляет продолжительность времени, которое поток ЦП тратит на вызов библиотеки в библиотеку времени выполнения CUDA, чтобы запустить ядро ​​(то есть поместить его в очередь запуска). Эта активность вызова библиотеки обычно связана с некоторыми временными издержками в диапазоне 5-50 микросекунд. Начало этого периода отмечается началом вызова в библиотеку. Конец этого периода отмечен временем, когда библиотека возвращает управление вашему коду (т. Е. Вашей следующей строке кода после запуска ядра).

Желтый блок графического процессора представляет фактический период времени, в течение которого ядро ​​выполнялось на графическом процессоре. Начало и конец этого желтого блока отмечены началом и концом активности ядра на графическом процессоре. Продолжительность здесь зависит от того, что делает код в вашем ядре, и сколько времени это занимает.

Я не думаю, что точная причина, по которой запуск ядра GPU занимает ~ 5-50 микросекунд процессорного времени, документирована или объяснена где-либо авторитетным образом, и это библиотека с закрытым исходным кодом, поэтому вам нужно будет признать, что накладные расходы, как то, что вы мало контролируете. Если вы разрабатываете ядра, которые работают долго и выполняют много работы, эти издержки могут стать незначительными.

...