在CUDA实际开发中,CUDA中API启动之前,存在隐藏的上下文初始化时间,这也是为什么在nvvp中查看时间线,发现第一个cudaMalloc时长200ms左右的原因。
这部分时间有时候相对于核函数运行时间较长,但又不能不预热GPU。
在大多数的CUDA示例中,都是先给主机端数据分配空间和初始化,再给设备端分配空间和数据传输。此时预热时间和主机端初始化时间是串行的。
int* a = new int[N];
int* b = new int[N];
for (size_t i = 0; i < N; i++)
{
a[i] = rand()/256;
b[i] = rand() / 256;
}
int* d_a;
int* d_b;
cudaMemcpy(d_a, a, sizeof(int) * N, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, sizeof(int) * N, cudaMemcpyHostToDevice);
为了隐藏掉预热时间,可以在主机上开启另一个并行的线程,让它去预热一下GPU。
int N = 1 << 24;
int* a = new int[N];
int* b = new int[N];
int* d_a;
int* d_b;
cudaSetDevice(0);
Timer t("CUDA + openMP");
#pragma omp parallel sections
{
#pragma omp section
{
printf("Using CPU thread %d.\n", omp_get_thread_num());
Timer t_c("CPU");
for (size_t i = 0; i < N; i++)
{
a[i] = i%7 + i % 17;
b[i] = (N - i)%7 + i % 37;
b[i] = (b[i]*a[i] * i % 107 - (N - i) % 7 + i % 17 + i % 37)/101;
}
t_c.log();
}
#pragma omp section
{
printf("Using CPU thread %d.\n", omp_get_thread_num());
Timer t_g("GPU");
cudaMalloc(&d_a, sizeof(int) * N);
cudaMalloc(&d_b, sizeof(int) * N);
t_g.log();
}
}
这样时间就优化掉了。
最后结果如下
CPU上计算时间将GPU上的cudaMalloc时间隐藏了。
并行那段的时间耗时与CPU端时间相同。