- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我注意到一个奇怪的现象,它允许您在 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
同样,如果您对这里发生的事情有任何想法,我们将不胜感激。
最佳答案
我明白为什么这可能有点令人困惑,但尽管您可能认为正在发生什么 Copy
永远不会在 GPU 上运行。 CopyKernel
在设备上被调用三次,但所有的启动都是在主机上启动的。方法如下。
首先需要了解内核是如何编译的,以及它们的启动是如何在 CUDA 运行时 API 中实际工作的。当 nvcc 编译你的 CopyKernel
和该内核的运行时 API 样式启动,发出一对 host 函数,如下所示:
void __device_stub__Z10CopyKernelPKiPi(const int *__par0, int *__par1)
{
if (cudaSetupArgument((void *)(char *)&__par0, sizeof(__par0), (size_t)0Ui64) != cudaSuccess) return;
if (cudaSetupArgument((void *)(char *)&__par1, sizeof(__par1), (size_t)8Ui64) != cudaSuccess) return;
{
volatile static char *__f;
__f = ((char *)((void ( *)(const int *, int *))CopyKernel));
(void)cudaLaunch(((char *)((void ( *)(const int *, int *))CopyKernel)));
};
}
void CopyKernel( const int *__cuda_0,int *__cuda_1)
{
__device_stub__Z10CopyKernelPKiPi( __cuda_0,__cuda_1);
}
这些提供了一个围绕必要的 API 调用的包装器,以将内核参数推送到 CUDA 驱动程序并启动内核。您会注意到内核的执行配置不在这些函数中处理。相反,每当 CopyKernel<<< >>>()
预处理器遇到调用,发出这种代码:
(cudaConfigureCall(1, 32)) ? (void)0 : (CopyKernel)(d_input, d_output);
即。内核启动配置被推送到驱动程序,然后调用包装函数,参数被推送到驱动程序并启动内核。
那么在 TestFunctionPointerLaunch
中发生了什么? ?基本上是一样的。这段代码
f<<<1,32>>>(d_input, d_output);
由 CUDA 前端预处理器编译成这个
(cudaConfigureCall(1, 32)) ? (void)0 : f(d_input, d_output);
即。内核启动的启动参数被推送到驱动程序,主机函数提供为 f
叫做。如果f
碰巧是一个内核包装函数(即 CopyKernel
),那么内核启动将通过包装器包含的 API 调用产生,否则不会。如果f
碰巧是一个主机函数,它本身包含一个运行时 API 内核调用(即 Copy
),然后 那个 主机代码将做同样的事情,并且最终会导致内核启动,只是进一步向下调用栈。
这就是您可以提供 CopyKernel
的方式或 Copy
作为 TestFunctionPointerLaunch
的参数它仍然有效。从技术上讲,这是未定义的行为,因为内核启动在 CUDA 运行时 API 内部工作的方式是故意不透明的,并且实现细节可能会随着时间的推移而改变。但现在它起作用了。
原因
Copy<<<1,32>>>(d_input, d_output);
没有编译,是因为Copy
是一个宿主函数,nvcc 可以在编译时检测到它——仅在语言规范中 __global__
功能可以启动并且编译器强制执行此检查。
但是当您传递一个函数指针时,编译器无法应用该检查。生成的代码恰好与宿主函数或宿主内核包装函数一起工作,因为运行时支持代码不会(并且可能不会)发出可以对函数指针执行内省(introspection)并识别函数指针的代码不会调用内核。所以语言规范要求被跳过,事情意外地起作用了。
我强烈建议不要尝试依赖这种行为。
关于c++ - CUDA 在使用函数指针时将主机函数作为内核启动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33580237/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!