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