sort_by_key是阻塞呼叫吗?

时间:2015-02-16 11:35:35

标签: cuda gpgpu thrust

我反复排队一系列内核:

for 1..100:
    for 1..10000:
        // Enqueue GPU kernels
        Kernel 1 - update each element of array  
        Kernel 2 - sort array  
        Kernel 3 - operate on array  
    end
    // run some CPU code
    output "Waiting for GPU to finish"
    // copy from device to host
    cudaMemcpy ... D2H(array)
end

内核3的顺序为O(N ^ 2),因此到目前为止是最慢的。对于内核2,我直接在设备上使用thrust :: sort_by_key:

thrust::device_ptr<unsigned int> key(dKey);
thrust::device_ptr<unsigned int> value(dValue);
thrust::sort_by_key(key,key+N,value);

似乎这种对推力的调用是阻塞的,因为只有在内部循环完成后才会执行CPU代码。我看到了这一点,因为如果我删除对sort_by_key的调用,主机代码(正确)在内循环结束前输出“Waiting”字符串,而如果我运行排序则不会。

有没有办法异步调用thrust::sort_by_key

1 个答案:

答案 0 :(得分:2)

  1. 首先考虑有一个内核启动队列,它只能容纳如此多的挂起启动。一旦启动队列已满,任何类型的其他内核启动都将被阻止。在空队列插槽可用之前,主机线程将不会继续(超出那些启动请求)。我很确定3次内核启动的10000次迭代将在它达到10000次迭代之前填充此队列。因此,如果您按顺序启动30000个内核,那么任何类型的非平凡内核启动都会有一些延迟(我认为)。 (最终,当所有内核都添加到队列中,因为有些内核已经完成,那么在所有内核实际完成之前,如果没有其他阻塞行为,你会看到“等待...”消息。)

  2. thrust::sort_by_key requires temporary storage(大小约等于您的数据集大小)。每次使用时,都会通过cudaMalloc操作分配此临时存储。此cudaMalloc操作阻止。从主机线程启动cudaMalloc时,它会等待内核活动中的间隙,然后才能继续。

  3. 要解决第2项,似乎可能至少有两种可能的方法:

    1. 提供thrust custom allocator。根据此分配器的特性,您可以消除阻塞cudaMalloc行为。 (但见下面的讨论)

    2. 使用cub SortPairs。这里的优势(我认为 - 你的例子不完整)是你可以做一次分配(假设你知道整个循环迭代中最坏的临时存储大小)并且不需要在你的内部进行临时内存分配环。

    3. 据我所知,推力方法(上面的1)在每次迭代时仍会有效地进行某种临时分配/自由步骤,即使你提供自定义分配器。如果你有一个设计良好的自定义分配器,那么它可能几乎是一个“无操作”。 cub方法似乎具有需要知道最大大小的缺点(为了完全消除分配/自由步骤的需要),但我认为对于推力自定义分配器将具有相同的要求。否则,如果你需要在某个时刻分配更多的内存,那么自定义分配器实际上必须做一些像cudaMalloc这样的事情,这会给工作带来麻烦。