How can I pass a C++ array of structs to a CUDA device?
我花了 2 天时间试图弄清楚这一点,但一无所获。假设我有一个看起来像这样的结构:
1 2 3 4
| struct Thing {
bool is_solid;
double matrix[9];
} |
我想创建一个名为 things 的结构数组,然后在 GPU 上处理该数组。类似于:
1 2 3 4 5 6 7
| Thing *things;
int num_of_things = 100;
cudaMallocManaged((void **)&things, num_of_things * sizeof(Thing));
// Something missing here? Malloc individual structs? Everything I try doesn't work.
things[10].is_solid = true; // Segfaults |
最好的做法是这样做而不是传递具有 num_of_things 大数组的单个结构?在我看来,这可能会变得非常讨厌,尤其是当你已经有数组时(比如 matrix,它需要是 9 * num_of_things.
任何信息将不胜感激!
- 你的代码对我来说很好。这是我的完整测试用例(你应该提供这样的东西)。你的方法没有错;这将是您在托管场景中的典型做法。您很可能会犯以下几个错误之一: 1. 您没有为 cc3.0 设备进行编译。 2.你没有在cc3.0设备上运行。 3. 您的环境不支持托管内存使用。
-
无论如何,最好的建议是在您遇到 cuda 代码问题时使用适当的 cuda 错误检查,我在测试用例中已经证明了这一点。尝试在我的测试用例中运行代码,看看你得到什么样的输出;我认为这将对正在发生的事情具有指导意义。
-
非常感谢!事实证明我做错了,因为编译器说没有足够的参数,所以我将第三个参数 0 传递给 cudaMallocManaged。我不认为它是相关的,所以我把它排除在外。它必须是 cudaMemAttachGlobal 或 cudaMemAttachHost,正如 freenode/#cuda 上一位非常友善的人指出的那样。
-
您使用的是 CUDA 7.5 吗?尽管文档在这方面尚不清楚,但编译器将/应该接受仅带有 2 个参数的 cudaMallocManaged 调用(正如您在这个问题中指出的那样,正如我在示例中指出的那样);在这种情况下,flags 变量将默认为 cudaMemAttachGlobal。如果您说"编译器说的参数不足",我只能假设这是 Visual Studio Intellisense? (实际上不是编译器)无论如何,您可以通过检查 cuda 包含目录中的 cuda_runtime.h 来说服自己相信 flags 参数的默认参数
-
谢谢,我检查了一下,它确实默认为 cuda_runtime.h 中的那个,但仍然不会只用 2 个 args 编译。不知道为什么。我的设置是纯命令行 g 、CMake 的 FindCuda 和带有标志 -gencode arch=compute_30,code=sm_30 的 nvcc。 IRC 的那个家伙提到这可能是 C 的东西,但我认为不是因为文件是 .cu 而其他一切都是 c 。
-
如果你使用我在那里提供的命令行(不使用 CMake)编译我在测试用例中提供的代码(第一条评论)会发生什么你仍然得到编译错误吗?
-
这也给了我"成功!"(尽管必须使用 sudo 运行 nvcc 来修复fatbinary fatal)。确实很奇怪。鉴于您的测试用例有效,我真的不知道为什么它不接受 2 个参数。
在评论中的一些对话之后,OP 发布的代码似乎没有问题。我能够成功编译并运行围绕该代码构建的测试用例,OP:
也是如此
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| $ cat t1005.cu
#include <iostream>
struct Thing {
bool is_solid;
double matrix[9];
};
int main(){
Thing *things;
int num_of_things = 100;
cudaError_t ret = cudaMallocManaged((void **)&things, num_of_things * sizeof(Thing));
if (ret != cudaSuccess) {
std::cout << cudaGetErrorString(ret) << std::endl;
return 1;}
else {
things[10].is_solid = true;
std::cout <<"Success!" << std::endl;
return 0;}
}
$ nvcc -arch=sm_30 -o t1005 t1005.cu
$ ./t1005
Success!
$ |
关于这个问题:
Is it even best practice to do it this way rather than pass a single struct with arrays that are num_of_things large?
是的,这是一种明智的做法,无论是否使用托管内存都可以使用。可以使用单个 cudaMemcpy 调用以简单的方式将一个或多或少任何不包含指向其他地方动态分配的数据的嵌入式指针的任何结构的数组传输到 GPU(例如,如果未使用托管内存。)
解决关于 cudaMallocManaged 的第三个 (flags) 参数的问题:
如果已指定,则传递零是不正确的(尽管 OP 发布的代码没有提供任何证据。)您应该使用记录的选择之一。
如果未指定,则仍然有效,并提供默认参数 cudaMemAttachGlobal。这可以通过查看 cuda_runtime.h 文件或简单地编译/运行上面的测试代码来确认。这一点似乎是文档中的一个疏忽,我已经向 NVIDIA 提交了一个内部问题来查看它。因此,文档可能会在未来对此进行更改。
最后,当您遇到 CUDA 代码问题时,始终需要进行适当的 cuda 错误检查,使用此类检查可能会发现所发生的任何错误。 OP 在代码注释中报告的段错误几乎可以肯定是由于 cudaMallocManaged 调用失败(可能是因为错误地提供了零参数),因此有问题的指针 (things) 没有实际分配。该指针的后续使用将导致段错误。我的测试代码演示了如何避免该段错误,即使 cudaMallocManaged 调用由于某种原因失败,关键是正确的错误检查。