在CUDA内核中调用operator new

时间:2015-02-27 01:40:33

标签: c++ cuda

在下文中,我试图在内核create_A()中为一个简单的对象A分配设备内存,然后从另一个内核delete_A()中删除它。在ptr_返回后,create_Acreate_A的分配似乎不会在设备内存中保留。

main1.cu

#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_作为全局变量,如下所示,它可以正常工作。

main2.cu

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_的分配仅在第二种情况下持续存在于设备上?

1 个答案:

答案 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或更高版本,则可以使用统一内存分配ptrcudaMallocManaged,并将其作为参考传递,如上所示。