CPU启动和Nvidia Profiling NVPROF的GPU启动的开始和结束边界在哪里?

Where is the boundary of start and end of CPU launch and GPU launch of Nvidia Profiling NVPROF?

CPU 和 GPU(黄色块)中内核启动开始和结束的定义是什么?它们的界限在哪里?

请注意 CPU 和 GPU 中那些黄色块的开始、结束和持续时间是 different.Why 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;
}

CPU黄块:

GPU 黄块:

请注意,您提到了 NVPROF,但您显示的图片来自 nvvp - 可视化分析器。 nvprof 是 command-line 分析器

GPU 内核启动是异步的。这意味着 CPU 线程启动内核但不等待内核完成。事实上,CPU activity 实际上是将内核放入启动队列 - 如果 GPU 上发生任何其他事情,内核的实际执行可能会延迟。

因此 CPU (API) activity 和 GPU activity 在时间方面没有明确的关系,除了 CPU 内核启动显然必须先于(至少稍微先于)GPU 内核执行。

CPU (API) 黄色块表示 CPU 线程在调用 CUDA 运行时库以启动内核(即将其放入启动队列)。这个库调用 activity 通常有一些与之相关的时间开销,在 5-50 微秒的范围内。此期间的开始以调用库的开始为标志。这段时间的结束以库 returns 控制您的代码(即内核启动后的下一行代码)的时间为标志。

GPU 黄色块表示内核在 GPU 上执行的实际时间段。这个黄色块的开始和结束在 GPU 上由 kernel activity 的开始和结束标记。这里的持续时间取决于内核中的代码正在做什么,以及需要多长时间。

我不认为 GPU 内核启动需要 ~5-50 微秒 CPU 时间的确切原因在任何地方以权威方式记录或解释,而且它是一个封闭源代码库,所以您将需要承认这种开销是您几乎无法控制的。如果你长期设计 运行 的内核并做很多工作,这个开销会变得微不足道。