c++ - 如何正确实现其成员同时从 Cuda/C++ 中的主机和设备代码调用的类?
问题描述
我有一个主机类TestClass
,它有一个指向类的指针作为成员,该类TestTable
的数据存储在 GPU 上的浮点数组中。
TestClass
调用访问内部数据的内核,TestTable
以及GetValue()
来自的方法TestClass
。
在阅读了很多内容并尝试了几个关于哪些类型说明符用于哪些方法和类以及如何(和在哪里)初始化TestTable
的选项之后,我觉得我的所有选项最终都归结为相同的内存访问错误。因此,我对 Cuda/C++ 工作原理的理解可能不足以正确实现它。我的代码应该如何正确设置?
这是我的最小版本的内容main.cu
:
#include <iostream>
#include <cuda_runtime.h>
#define CUDA_CHECK cuda_check(__FILE__,__LINE__)
inline void cuda_check(std::string file, int line)
{
cudaError_t e = cudaGetLastError();
if (e != cudaSuccess) {
std::cout << std::endl
<< file << ", line " << line << ": "
<< cudaGetErrorString(e) << " (" << e << ")" << std::endl;
exit(1);
}
}
class TestTable {
float* vector_;
int num_cells_;
public:
void Init() {
num_cells_ = 1e4;
cudaMallocManaged(&vector_, num_cells_*sizeof(float));
CUDA_CHECK;
}
void Free() {
cudaFree(vector_);
}
__device__
bool UpdateValue(int global_index, float val) {
int index = global_index % num_cells_;
vector_[index] = val;
return false;
}
};
class TestClass {
private:
float value_;
TestTable* test_table_;
public:
TestClass() : value_(1.) {
// test_table_ = new TestTable;
cudaMallocManaged(&test_table_, sizeof(TestTable));
test_table_->Init();
CUDA_CHECK;
}
~TestClass() {
test_table_->Free();
cudaFree(test_table_);
CUDA_CHECK;
}
__host__ __device__
float GetValue() {
return value_;
}
__host__
void RunKernel();
};
__global__
void test_kernel(TestClass* test_class, TestTable* test_table) {
int index = threadIdx.x + blockIdx.x * blockDim.x;
int stride = blockDim.x * gridDim.x;
for (int i = index; i < 1e6; i += stride) {
const float val = test_class->GetValue();
test_table->UpdateValue(i, val);
}
}
__host__
void TestClass::RunKernel() {
test_kernel<<<1,1>>>(this, test_table_);
cudaDeviceSynchronize(); CUDA_CHECK;
}
int main(int argc, char *argv[]) {
TestClass* test_class = new TestClass();
std::cout << "TestClass successfully constructed" << std::endl;
test_class->RunKernel();
std::cout << "Kernel successfully run" << std::endl;
delete test_class;
std::cout << "TestClass successfully destroyed" << std::endl;
return 0;
}
我得到的错误是line 88: an illegal memory access was encountered (700)
.
我认为错误在于以下问题之一:
TestTable
没有使用 正确创建new
,这可能很糟糕。但是,取消注释test_table_ = new TestTable;
并TestClass()
不能解决问题。GetValue()
intest_kernel
不返回有效的浮点变量。如果我用任意浮点数替换它,例如1.f
,程序运行没有错误。但是,在我的代码的真实(不是最小)版本中GetValue()
,会在代码库的不同点进行大量计算,因此硬编码不是一种选择。- 我从不复制
TestClass
到 GPU,而是从内核调用它的成员函数之一。我看到这一定会造成麻烦,但我觉得知道在哪里以及如何复制它并不直观。如果我只调用GetValue()
内核而不重用其结果,则不会出现错误,因此似乎我的程序可以调用GetValue()
而无需将类复制到GPU。
我无法应用于我的具体问题的可能相关问题:
- 从 cuda 内核中访问类数据成员 - 如何设计正确的主机/设备交互?- 这个看起来非常相似,但不知何故我无法将它翻译成我的用例。
- 在不同的 CUDA 内核中访问类成员- 在这里,我不确定我有两个类相互“交互”这一事实会如何影响解决方案。
- CUDA and Classes - 这个问题对我来说似乎更笼统。
非常感谢任何帮助!
解决方案
这里的问题与您如何分配有关TestClass
:
TestClass* test_class = new TestClass();
test_class
现在是指向主机内存的普通指针。如果您打算在设备代码中使用该指针:
void TestClass::RunKernel() {
test_kernel<<<1,1>>>(this, test_table_);
^^^^
和:
void test_kernel(TestClass* test_class, TestTable* test_table) {
int index = threadIdx.x + blockIdx.x * blockDim.x;
int stride = blockDim.x * gridDim.x;
for (int i = index; i < 1e6; i += stride) {
const float val = test_class->GetValue();
^^^^^^^^^^
那是行不通的。在 CUDA 中,取消引用设备代码中的主机指针通常是一个基本问题。
new
对于顶级类,我们可以通过使用带有托管分配器的放置来解决此问题:
//TestClass* test_class = new TestClass();
TestClass* test_class;
cudaMallocManaged(&test_class, sizeof(TestClass));
new(test_class) TestClass();
当我们这样做时,还需要更改释放器。正如评论中所指出的,您还应该确保在 de-allocation 之前调用析构函数:
// delete test_class;
test_class->~TestClass();
cudaFree(test_class);
当我进行这些更改时,您的代码运行时不会出现运行时错误。
推荐阅读
- python - 如何解决我的代码中的输出问题?
- python - 操作系统升级后 Python Selenium Chrome 未运行
- html - 使页脚位置粘在最后
- c# - EPPlus - 读取对象
- google-apps-script - 删除触发器自动删除了我的脚本项目
- webpack - 有没有一个插件可以通过webpack缩小微信小程序中的wxss和js/wxs?
- html - 设置网格列始终为 100%
- html - 文字不想在图像下方。使用引导程序
- android - Android Material 文本输入布局结束图标不可见但工作
- c++ - 在 Openmp 中有没有办法找出主线程分配到的位置?