C++ 从静态初始化代码启动CUDA内核时出现问题

C++ 从静态初始化代码启动CUDA内核时出现问题,c++,cuda,global-variables,static-initialization,C++,Cuda,Global Variables,Static Initialization,我有一个类在其构造函数中调用内核,如下所示: “ScalarField.h” #包括 无效错误检查(cudaError\u t err,const char*msg){ if(err!=cudaSuccess){ std::cout短版本: 当class A在main之外实例化时出现问题的根本原因是,在调用class A的构造函数之前,没有运行使用内核初始化CUDA运行时库所需的特定钩子例程。发生这种情况是因为无法保证顺序其中静态对象在C++执行模型中被实例化和初始化。全局范围类在初始化CUDA

我有一个类在其构造函数中调用内核,如下所示:

“ScalarField.h”

#包括
无效错误检查(cudaError\u t err,const char*msg){
if(err!=cudaSuccess){

std::cout短版本:

class A
在main之外实例化时出现问题的根本原因是,在调用
class A
的构造函数之前,没有运行使用内核初始化CUDA运行时库所需的特定钩子例程。发生这种情况是因为无法保证顺序其中静态对象在C++执行模型中被实例化和初始化。全局范围类在初始化CUDA设置的全局作用域对象之前被实例化。内核代码在调用之前从未加载到上下文中,并且运行时错误导致。 据我所知,这是CUDA运行时API的一个真正的限制,用户代码中不容易修复。在您的小例子中,您可以用调用
cudaMemset
或一个非基于符号的运行时API memset函数来替换内核调用,它会起作用。这个问题完全限于用户内核在运行时通过运行时API加载的或设备符号。因此,空的默认构造函数也可以解决您的问题。从设计角度来看,我非常怀疑构造函数中调用内核的任何模式。为不依赖默认构造函数或de的类GPU安装/拆卸添加特定方法IMHO表示,structor将是一种更干净、更不容易出错的设计

详细信息:

有一个内部生成的例程(
\uuu cudaRegisterFatBinary
),必须运行该例程,才能使用CUDA驱动程序API加载和注册任何运行时API程序的fatbin负载中包含的内核、纹理和静态定义的设备符号,然后才能正确调用内核。这是“延迟”的一部分运行时API的上下文初始化功能。您可以自己确认,如下所示:

下面是您发布的修订示例的gdb跟踪。注意,我在
\uu cudaRegisterFatBinary
中插入了一个断点,在调用静态
构造函数和内核启动失败之前,无法到达该断点:

talonmies@box:~$ gdb a.out 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/talonmies/a.out...done.
(gdb) break '__cudaRegisterFatBinary' 
Breakpoint 1 at 0x403180
(gdb) run
Starting program: /home/talonmies/a.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Scalar Field
[New Thread 0x7ffff5a63700 (LWP 10774)]
Class A
Kernel : invalid device function 
[Thread 0x7ffff5a63700 (LWP 10774) exited]
[Inferior 1 (process 10771) exited with code 0377]

如果这对您来说真的是一个严重的问题,我建议您联系NVIDIA开发人员支持部门并提交一份错误报告。

您是否可以提供更多代码来帮助回答这些问题:a类在它自己的文件中,但内核在另一个文件中,文件扩展名是什么,等等。您应该提供足够的代码让其他人能够复制您的问题。@Noel Perez Gonzalez如果您将
a_Object
定义为全局变量,它将在全局数据初始化期间开始执行。这是一种非常糟糕的做法,因为无法知道执行顺序。记住这一点,初始化所有CUDA内容的代码可能比全局变量运行得晚ata.用更多代码更新了问题(请注意,我没有编译)@Raxvan谢谢你的建议,我只是认为运行时顺序和编译顺序是一样的。事实上,你的代码片段似乎不完整,请不要编译。请发布最小大小的代码,让其他人可以从他身边复制、粘贴、编译和运行,指出你的代码工作时的情况和你的代码不工作时的情况。@JackOLantern I编辑了代码,但需要同行审查。回答很好。对于“全局”初始化的推力对象也会发生同样的情况吗?@Jackolanten:这个问题将限于任何在其构造函数中调用内核的对象。如果推力对象在构造期间调用内核,那么应该会发生(我想device_vector是一个候选,它在实例化过程中设置了一个默认值,尽管我已经很长时间没有查看源代码了。)谢谢你的补充。这是我的第700次(可能是最后一次)继续回答。我不认为你需要停止回答StackOverflow。不时会出现有趣的问题。是的,我也希望你能继续。也许我们其他人可以处理“渣滓”,你可以接受更难的。:-)@talonmies:在调用cuda API函数之前,是否有运行时API函数在主机代码中验证cuda运行时基础设施是否已建立(且尚未破坏)?
#include "ScalarField.h"


static __global__ void KernelSetScalarField(ScalarField v) {
    int index = threadIdx.x + blockIdx.x * blockDim.x;
    if (index < v.dimension) v.array[index] = 0.0f;
}

class A {
public:
    ScalarField v;

    A(): v(ScalarField(3)) {
        std::cout << "Class A" << std::endl;
        KernelSetScalarField<<<1, 32>>>(v);
        ERROR_CHECK(cudaGetLastError(),"Kernel");
    }
};
#include "classA.h"

A a_object;

int main() {
    std::cout << "Main" << std::endl;
    return 0;
}
talonmies@box:~$ gdb a.out 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/talonmies/a.out...done.
(gdb) break '__cudaRegisterFatBinary' 
Breakpoint 1 at 0x403180
(gdb) run
Starting program: /home/talonmies/a.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Scalar Field
[New Thread 0x7ffff5a63700 (LWP 10774)]
Class A
Kernel : invalid device function 
[Thread 0x7ffff5a63700 (LWP 10774) exited]
[Inferior 1 (process 10771) exited with code 0377]
talonmies@box:~$ cat main.cu
#include "classA.h"


int main() {
    A a_object;
    std::cout << "Main" << std::endl;
    return 0;
}

talonmies@box:~$ nvcc --keep -arch=sm_30 -g main.cu
talonmies@box:~$ gdb a.out 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/talonmies/a.out...done.
(gdb) break '__cudaRegisterFatBinary' 
Breakpoint 1 at 0x403180
(gdb) run
Starting program: /home/talonmies/a.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x0000000000403180 in __cudaRegisterFatBinary ()
(gdb) cont
Continuing.
Scalar Field
[New Thread 0x7ffff5a63700 (LWP 11084)]
Class A
Main
[Thread 0x7ffff5a63700 (LWP 11084) exited]
[Inferior 1 (process 11081) exited normally]