在cuda内核中创建一个向量

时间:2016-02-13 21:35:54

标签: c++ cuda

我需要创建某种堆栈来抓取cuda内核中的树。我以为我可以使用thrust :: device_vector,但显然不是。是否有api或者我必须自己编写代码。

__global__ 
void step_objects_kernel(ContainerNode* root, ObjectNode** objs, ObjectNode* new_objs, size_t n, real dt, real g)
{
    int idx = blockIdx.x * gridDim.x + threadIdx.x;
    if(idx >= n) return;

    thrust::device_vector<Node*> to_visit;
    to_visit.push_back(root);

    vec3 a = {0};
    while(!to_visit.empty())
    {
        Node* n = to_visit.back();
        to_visit.pop_back();

    }
}
error: calling a __host__ function("thrust::device_vector<Node *, thrust::device_malloc_allocator<Node *> > ::device_vector") from a __global__ function("step_objects_kernel") is not allowed

1 个答案:

答案 0 :(得分:2)

{1}在CUDA设备代码中不可用是正确的。

我不知道任何内核容器类API是CUDA发布本身的一部分。但是,如果你四处搜索,你可能会发现许多可能有用/有趣的实现。像trove这样的低级库可能会为这种用例提供​​更好的性能。

在您的示例中,似乎每个线程都将维护自己的&#34;堆栈&#34;或&#34; vector&#34;跟踪树遍历。 (我将在这里提供的方法取决于没有线程同时访问同一个堆栈。如果你需要从多个线程进行并发访问,方法here可能是一个有用的起点。)

如果您知道这样一个堆栈的最大可能大小是多少,我建议提前为它分配一个静态(本地)变量定义每线程内核,或动态分配,例如通过thrust::device_vector。 (我不建议内核cudaMalloc这样做,出于性能原因,我绝对不会建议动态分配/解除分配。)选择哪种分配方法可以提供最高性能可能取决于你的实际测试用例。合并规则(即底层存储方法)对于访问全局指针与访问本地指针有些不同。如果您的线程倾向于在warp中均匀地推送或弹出,并且随着代码的进展,那么任一分配方法都可以提供良好的性能。您可以尝试这两种方法。

这是&#34;堆栈的一个相当简单的部分工作示例&#34;您在示例中概述的方法,假设每个线程的最大堆栈大小已知 a priori 。它并没有经过全面测试;我的目的是给你一些想法或一个起点。但是,如果您发现错误,请随时指出它们,我会尝试解决它们。

malloc

注意:

  1. 除了您在此处看到的内容之外,此代码未经过测试。我建议在按原样使用之前进行更多验证。

  2. 正如您在代码中看到的那样,基本上没有错误检查。

  3. 这种随机访问通常会很慢,可能与您选择的分配方法无关。如果可能的话,尽量减少使用这种&#34;堆栈&#34;。如果您知道每个线程的堆栈大小非常小,您也可以尝试使用此构造进行$ cat t1082.cu const size_t max_items = 256; template <typename T> class cu_st{ // simple implementation of "stack" function T *my_ptr; size_t n_items; size_t my_width; public: __host__ __device__ cu_st(T *base, size_t id, size_t width=0){ if (width == 0){ // "local" stack allocated my_ptr = base; my_width = 1;} else{ // "global" stack allocated my_ptr = base + id; my_width = width;} n_items = 0;} __host__ __device__ int push_back(T &item){ if (n_items < max_items){ *my_ptr = item; my_ptr += my_width; n_items++; return 0;} return -1;} __host__ __device__ T pop_back(){ if (n_items > 0){ n_items--; my_ptr -= my_width;} return *my_ptr;} __host__ __device__ T back(){ if (n_items > 0){ return *(my_ptr-my_width);} return *my_ptr;} __host__ __device__ bool empty(){ return (n_items == 0);} __host__ __device__ size_t size(){ return n_items;} __host__ __device__ size_t max_size(){ return max_items;} }; const size_t nTPB = 256; const size_t nBLK = 256; typedef int Node; __global__ void kernel(Node **g_stack, size_t n) { int idx = blockIdx.x * gridDim.x + threadIdx.x; if(idx >= n) return; Node *root = NULL; //method 1 - global stack cu_st<Node*> to_visit(g_stack, idx, gridDim.x*blockDim.x); to_visit.push_back(root); while(!to_visit.empty()) { Node* n = to_visit.back(); to_visit.pop_back(); } //method 2 - local stack Node *l_stack[max_items]; cu_st<Node*> l_to_visit(l_stack, idx); l_to_visit.push_back(root); while(!l_to_visit.empty()) { Node* n = l_to_visit.back(); l_to_visit.pop_back(); } } int main(){ Node **d_stack; cudaMalloc(&d_stack, nTPB*nBLK*max_items*sizeof(Node *)); kernel<<<nBLK, nTPB>>>(d_stack, nTPB*nBLK); cudaDeviceSynchronize(); } $ nvcc -o t1082 t1082.cu $ cuda-memcheck ./t1082 ========= CUDA-MEMCHECK ========= ERROR SUMMARY: 0 errors $ 内存分配。

  4. 我在这里没有展示的另一种分配方法是给每个线程一个全局分配,但是线程是push和pop连续而不是我在这里展示的跨步方式(算法上是两种方法的组合)我在这里概述了)。这样的方法肯定会降低&#34;制服中的性能。案例,但可能会在某些&#34;随机&#34;访问模式。