Thrust:transform_reduce:unary_op.operator中的cudaMalloc

时间:2012-12-30 21:15:47

标签: cuda gpgpu thrust

在我的unary_op.operator中,我需要创建一个临时数组 我想cudaMalloc是要走的路 但是,它是否具有性能效率还是有更好的设计?

struct my_unary_op
{
    __host__ __device__ int operator()(const int& index) const
    {
        int* array;
        cudaMalloc((void**)&array, 10*sizeof(int));

        for(int i = 0; i < 10; i++)
            array[i] = index;

        int sum=0;
        for(int i=0; i < 10 ; i++)
            sum += array[i];

        return sum;
    };

};
int main()
{
    thrust::counting_iterator<int> first(0);
    thrust::counting_iterator<int> last = first+100;

    my_unary_op unary_op = my_unary_op();

    thrust::plus<int> binary_op;

    int init = 0;
    int sum = thrust::transform_reduce(first, last, unary_op, init, binary_op);

    return 0;
};

1 个答案:

答案 0 :(得分:2)

您将无法在cudaMalloc()函数中编译__device__,因为它是仅限主机的函数。但是,您可以使用普通malloc()new(在计算能力&gt; = 2.0的设备上),但这些在设备上运行时效率不高。有两个原因。第一个是在内存分配调用期间并发运行的线程被序列化。第二个是调用以块的方式分配全局内存,这些块以这样的方式排列:当warp中的32个线程运行内存加载和存储指令时,它们不相邻,因此你没有得到正确的合并内存访问。

您可以在__device__函数中使用固定大小的C样式数组(即int array[10];)来解决这两个问题。小型固定大小的数组有时可以通过编译器进行优化,以便将它们存储在寄存器文件中,以实现极快的访问速度。如果编译器将它们存储在全局内存中,它将使用本地内存。本地内存存储在全局内存中,但它以交错方式交错,当warp中的32个线程运行加载或存储指令时,每个线程访问内存中的相邻位置,使事务完全合并。

如果您在运行时不知道C数组的大小是多少,请在数组中分配一个最大大小并保留一些未使用的大小。

我认为固定大小的数组使用的内存总量将取决于GPU上并发处理的线程总数,而不是内核启动的线程总数。在this answer @mharris中显示了如何计算最大可能的并发线程数,对于GTX580来说是24,576。因此,如果固定大小的数组是16个32位值,则阵列使用的最大内存量为1536KiB。

如果需要多种数组大小,可以使用模板编译具有多种不同大小的内核。然后,在运行时,选择一个能够容纳您需要的大小。但是,如果您只是简单地分配可能需要的最大值,则内存使用率可能不会是您可以启动的线程数量的限制因素。