【发布时间】:2015-11-07 07:17:03
【问题描述】:
我注意到一个奇怪的现象,它允许您在 CUDA 中使用三尖括号表示法来启动主机函数。为了测试这一点,我编写了一个简单的内核,在两个整数数组之间复制数据。请注意,我在 Tesla K40 上运行所有这些代码并使用 -gencode arch=compute_35,code=sm_35 进行编译:
#ifndef HOST_LAUNCH_H
#define HOST_LAUNCH_H
using namespace std;
// Assumes input and output are both length 32
__global__ void CopyKernel(const int* input, int* output) {
size_t global_idx = blockIdx.x * blockDim.x + threadIdx.x;
output[global_idx] = input[global_idx];
}
__host__ void Copy(const int* input, int* output) {
int* d_input = 0;
int* d_output = 0;
cudaMalloc((void**)&d_input, 32 * sizeof(int));
cudaMalloc((void**)&d_output, 32 * sizeof(int));
cudaMemcpy(d_input, input, 32 * sizeof(int), cudaMemcpyHostToDevice);
CopyKernel<<<1,32>>>(d_input, d_output);
cudaMemcpy(output, d_output, 32 * sizeof(int), cudaMemcpyDeviceToHost);
cudaFree(d_input);
cudaFree(d_output);
}
#endif
然后我编写了以下单元测试:
#include "host_launch.h"
#include <assert.h>
using namespace std;
__host__ void TestKernelLaunch() {
int input[32];
int output[32];
for(int i = 0; i < 32; i++) {
input[i] = i;
output[i] = 0;
}
int* d_input = 0;
int* d_output = 0;
cudaMalloc((void**)&d_input, 32 * sizeof(int));
cudaMalloc((void**)&d_output, 32 * sizeof(int));
cudaMemcpy(d_input, input, 32 * sizeof(int), cudaMemcpyHostToDevice);
for(int i = 0; i < 32; i++) {
assert(output[i] == 0);
}
CopyKernel<<<1,32>>>(d_input, d_output);
cudaMemcpy(output, d_output, 32 * sizeof(int), cudaMemcpyDeviceToHost);
for(int i = 0; i < 32; i++) {
assert(output[i] == i);
}
cudaFree(d_input);
cudaFree(d_output);
}
__host__ void TestHostLaunch() {
int input[32];
int output[32];
for(int i = 0; i < 32; i++) {
input[i] = i + 1;
output[i] = 0;
}
int* d_input = 0;
int* d_output = 0;
cudaMalloc((void**)&d_input, 32 * sizeof(int));
cudaMalloc((void**)&d_output, 32 * sizeof(int));
cudaMemcpy(d_input, input, 32 * sizeof(int), cudaMemcpyHostToDevice);
for(int i = 0; i < 32; i++) {
assert(output[i] == 0);
}
//Copy<<<1,32>>>(d_input, d_output);
cudaMemcpy(output, d_output, 32 * sizeof(int), cudaMemcpyDeviceToHost);
for(int i = 0; i < 32; i++) {
assert(output[i] == i + 1);
}
cudaFree(d_input);
cudaFree(d_output);
}
__host__ void TestFunctionPointerLaunch(void (*f)(const int*, int*)) {
int input[32];
int output[32];
for(int i = 0; i < 32; i++) {
input[i] = i + 2;
output[i] = 0;
}
int* d_input = 0;
int* d_output = 0;
cudaMalloc((void**)&d_input, 32 * sizeof(int));
cudaMalloc((void**)&d_output, 32 * sizeof(int));
cudaMemcpy(d_input, input, 32 * sizeof(int), cudaMemcpyHostToDevice);
for(int i = 0; i < 32; i++) {
assert(output[i] == 0);
}
f<<<1,32>>>(d_input, d_output);
cudaMemcpy(output, d_output, 32 * sizeof(int), cudaMemcpyDeviceToHost);
for(int i = 0; i < 32; i++) {
assert(output[i] == i + 2);
}
cudaFree(d_input);
cudaFree(d_output);
}
int main() {
TestKernelLaunch();
TestFunctionPointerLaunch(CopyKernel);
TestFunctionPointerLaunch(Copy);
}
如果我取消注释该行:
//Copy<<<1,32>>>(d_input, d_output);
我明白了:
host_launch_unittest.cu(49): error: a host function call cannot be configured
但等效的执行方式是:
f<<<1,32>>>(d_input, d_output);
在 TestFunctionPointerLaunch 中,它通过了所有的断言。我只是想知道 GPU 到底在做什么,才能使这个主机功能启动正常运行。我编写了这些测试来隔离行为,但也发现它适用于更复杂的内核/主机功能。另外,我决定对这些进行计时,看看它们是否以某种方式编译为等效操作:
#include "host_launch.h"
#include <iostream>
#include <assert.h>
using namespace std;
__host__ float MeanCopyTime(const int copy_count, void (*f)(const int*, int*)) {
int input[32 * copy_count];
int output[32 * copy_count];
for(int i = 0; i < 32 * copy_count; i++) {
input[i] = i;
output[i] = 0;
}
int* d_input = 0;
int* d_output = 0;
cudaMalloc((void**)&d_input, 32 * copy_count * sizeof(int));
cudaMalloc((void**)&d_output, 32 * copy_count * sizeof(int));
cudaMemcpy(d_input, input, 32 * copy_count * sizeof(int), cudaMemcpyHostToDevice);
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start);
for(int i = 0; i < copy_count; i++)
f<<<1,32>>>(d_input + i * 32, d_output + i * 32);
cudaEventRecord(stop);
cudaEventSynchronize(stop);
float msecs = 0;
cudaEventElapsedTime(&msecs, start, stop);
cudaMemcpy(output, d_output, 32 * copy_count * sizeof(int), cudaMemcpyDeviceToHost);
cudaFree(d_input);
cudaFree(d_output);
for(int i = 0; i < 32 * copy_count; i++) {
assert(output[i] == i);
}
return msecs / copy_count;
}
int main() {
int copy_count = 10000;
cout << endl;
cout << "Average Kernel Launch Time: " << MeanCopyTime(copy_count, CopyKernel) << endl;
cout << "Average Host Function Launch Time: " << MeanCopyTime(copy_count, Copy) << endl;
cout << endl;
}
对于我的架构,返回:
Average Kernel Launch Time: 0.00420756
Average Host Function Launch Time: 0.169097
再次感谢您对这里发生的事情的任何想法。
【问题讨论】: