在下文中,我试图在内核create_A()
中为一个简单的对象A
分配设备内存,然后从另一个内核delete_A()
中删除它。在ptr_
返回后,create_A
中create_A
的分配似乎不会在设备内存中保留。
#include <stdio.h>
#include <cuda.h>
class A {
public:
int a;
__device__ A(int x)
: a(x) {
printf("\nA()");
}
__device__ ~A() { printf("\n~A()"); }
};
__global__ void create_A(A* ptr_) {
printf("\nCreating A...");
ptr_ = new A(7);
return;
}
__global__ void delete_A(A* ptr_) {
printf("\nA.a = %d \nDeleting A...", ptr_->a);
delete ptr_;
return;
}
int main(void) {
A* ptr_ = NULL;
create_A<<<1,1>>>(ptr_);
delete_A<<<1,1>>>(ptr_);
cudaDeviceSynchronize();
cudaError_t cudaerr = cudaDeviceSynchronize();
if (cudaerr != CUDA_SUCCESS)
printf("kernel launch failed with error \"%s\".\n",
cudaGetErrorString(cudaerr));
return 0;
}
输出:
Creating A...
A()
kernel launch failed with error "an illegal memory access was encountered".
另一方面,如果我将ptr_
作为全局变量,如下所示,它可以正常工作。
class A {
public:
int a;
__device__ A(int x)
: a(x) {
printf("\nA()");
}
__device__ ~A() { printf("\n~A()"); }
};
__device__ A* ptr_;
__global__ void create_A() {
printf("\nCreating A...");
ptr_ = new A(7);
return;
}
__global__ void delete_A() {
printf("A.a is: %d", ptr_->a);
printf("\nDeleting A...");
delete ptr_;
return;
}
int main(void) {
create_A<<<1,1>>>();
delete_A<<<1,1>>>();
cudaDeviceSynchronize();
cudaError_t cudaerr = cudaDeviceSynchronize();
if (cudaerr != CUDA_SUCCESS)
printf("kernel launch failed with error \"%s\".\n",
cudaGetErrorString(cudaerr));
return 0;
}
输出:
Creating A...
A()
A.a is: 7
Deleting A...
~A()
为什么ptr_
的分配仅在第二种情况下持续存在于设备上?
答案 0 :(得分:4)
在create_A返回后,似乎create_A中ptr_的分配不会在设备内存中保留。
它 持续存在,但你正在失去指向它的指针。您似乎不熟悉C ++范围规则。考虑以下(非CUDA)C ++示例:
void allocate(A* ptr) {
// modifies a local copy of ptr, not the one in caller's scope
ptr = new A(7);
} // pointer is lost here, memory leak
A* ptr = nullptr;
allocate(ptr);
ptr->something(); // error: ptr is still null in this scope
简而言之,您已将null
传递给delete_A
:
printf("A.a is: %d", ptr_->a); // ptr_ is null, access violation
在常规C ++中,您只需通过引用传递指针(或按值返回)来解决问题:
void allocate(A*& ptr) {
// modifies ptr in caller's scope
ptr = new A(7);
}
A* ptr = nullptr;
allocate(ptr);
ptr->something(); // works fine!
如果你正在使用CUDA&lt; 6.5,你不能这样做,因为内核参数可能不是引用。您需要将指针存储为全局(如您所做),或者为其分配内存并将其从设备复制到设备。
如果您使用的是CUDA 6.5或更高版本,则可以使用统一内存分配ptr
到cudaMallocManaged
,并将其作为参考传递,如上所示。