CUDA减少了许多小型,不规则大小的阵列

时间:2009-11-20 22:47:30

标签: arrays data-structures cuda parallel-processing cub

我想知道是否有人可以提出最佳方法来计算CUDA中大量相对较小但不同大小的数组的均值/标准偏差?

SDK中的并行缩减示例适用于单个非常大的数组,看起来大小很方便是每个块的线程数的倍数,但我的情况却大不相同:

从概念上讲,我有大量的对象,每个对象包含两个组件upperlower,每个组件都有一个x和一个y坐标。即。

upper.x, lower.x, upper.y, lower.y

这些数组中的每一个长度大约为800,但它在对象之间(不在对象内)之间变化,例如。

Object1.lower.x = 1.1, 2.2, 3.3
Object1.lower.y = 4.4, 5.5, 6.6
Object1.upper.x = 7.7, 8.8, 9.9
Object1.upper.y = 1.1, 2.2, 3.3

Object2.lower.x = 1.0,  2.0,  3.0,  4.0, 5.0 
Object2.lower.y = 6.0,  7.0,  8.0,  9.0, 10.0
Object2.upper.x = 11.0, 12.0, 13.0, 14.0, 15.0 
Object2.upper.y = 16.0, 17.0, 18.0, 19.0, 20.0

请注意以上只是我表示数组的方式,我的数据没有存储在C结构或类似内容中:数据可以按我需要的方式组织。关键是,对于每个数组,需要计算平均值,标准偏差和最终的直方图,并且在一个特定对象中,需要计算数组之间的比率和差异。

我应该如何将这些数据发送到GPU设备并组织我的线程块层次结构?我有一个想法是将所有数组填充为零以使它们具有相同的长度,并且有一组块在每个对象上工作,但是如果它可以工作的话,它似乎有各种各样的问题。

提前致谢

2 个答案:

答案 0 :(得分:1)

如果我理解正确,您希望将Object1.lower.x减少为一个结果,将Object1.lower.y减少为另一个结果,依此类推。对于任何给定的对象,有四个要减少的数组,所有数组的长度都相等(对于对象)。

有许多可能的方法,一个影响因素是系统中的对象总数。我假设这个数字很大。

为获得最佳性能,您需要最佳的内存访问模式,并希望避免分歧。由于全等数组的数量是4,如果你采用天真的做法,每个线程做一个数组,下面,你不仅会遭受糟糕的内存访问,而且h / w需要检查每次迭代哪些线程在warp需要执行循环 - 那些不会被禁用的循环可能效率低下(例如,如果一个数组比其他数组长得多)。

for (int i = 0 ; i < myarraylength ; i++)
    sum += myarray[i];

相反,如果你让每个warp对一个数组求和,那么它不仅会更有效,而且你的内存访问模式也会好得多,因为相邻的线程会读取相邻的元素[1]。

for (int i = tidwithinwarp ; i < warparraylength ; i += warpsize)
{
    mysum += warparray[i];
}
mysum = warpreduce(mysum);

您还应该考虑数组的对齐,最好在64字节边界上对齐,但如果您正在开发1.2或更高的计算能力,那么这不如旧GPU那么重要。

在这个示例中,您将为每个块启动四个warp,即128个线程,以及与对象一样多的块。

[1]你的确可以说你可以选择你喜欢的任何内存排列,通常它可以用来交错数组,这样数组[0] [0]就在数组[1] [0]旁边,因为这意味着相邻线程可以在相邻阵列上运行并获得合并访问。然而,由于数组的长度不是常数,这可能很复杂,需要填充较短的数组。

答案 1 :(得分:1)

作为Tom回答的后续内容,我想提一下CUB可以轻松实现 warp reduction

这是一个有效的例子:

#include <cub/cub.cuh>
#include <cuda.h>

#include "Utilities.cuh"

#include <iostream>

#define WARPSIZE    32
#define BLOCKSIZE   256

const int N = 1024;

/*************************/
/* WARP REDUCTION KERNEL */
/*************************/
__global__ void sum(const float * __restrict__ indata, float * __restrict__ outdata) {

    unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x;

    unsigned int warp_id = threadIdx.x / WARPSIZE;

    // --- Specialize WarpReduce for type float. 
    typedef cub::WarpReduce<float, WARPSIZE> WarpReduce;

    // --- Allocate WarpReduce shared memory for (N / WARPSIZE) warps
    __shared__ typename WarpReduce::TempStorage temp_storage[BLOCKSIZE / WARPSIZE];

    float result;
    if(tid < N) result = WarpReduce(temp_storage[warp_id]).Sum(indata[tid]);

    if(tid % WARPSIZE == 0) outdata[tid / WARPSIZE] = result;
}

/********/
/* MAIN */
/********/
int main() {

    // --- Allocate host side space for 
    float *h_data       = (float *)malloc(N * sizeof(float));
    float *h_result     = (float *)malloc((N / WARPSIZE) * sizeof(float));

    float *d_data;      gpuErrchk(cudaMalloc(&d_data, N * sizeof(float)));
    float *d_result;    gpuErrchk(cudaMalloc(&d_result, (N / WARPSIZE) * sizeof(float)));

    for (int i = 0; i < N; i++) h_data[i] = (float)i;

    gpuErrchk(cudaMemcpy(d_data, h_data, N * sizeof(float), cudaMemcpyHostToDevice));

    sum<<<iDivUp(N, BLOCKSIZE), BLOCKSIZE>>>(d_data, d_result);
    gpuErrchk(cudaPeekAtLastError());
    gpuErrchk(cudaDeviceSynchronize());

    gpuErrchk(cudaMemcpy(h_result, d_result, (N / WARPSIZE) * sizeof(float), cudaMemcpyDeviceToHost));

    std::cout << "output: ";
    for(int i = 0; i < (N / WARPSIZE); i++) std::cout << h_result[i] << " ";
    std::cout << std::endl;

    gpuErrchk(cudaFree(d_data));
    gpuErrchk(cudaFree(d_result));

    return 0;
}

在此示例中,创建了一个长度为N的数组,结果为32个连续元素的总和。所以

result[0] = data[0] + ... + data[31];
result[1] = data[32] + ... + data[63];
....