【问题标题】:Get statistics for a list of numbers using GPU使用 GPU 获取数字列表的统计信息
【发布时间】:2012-02-27 04:15:53
【问题描述】:

我在一个文件上有几个数字列表。例如,

.333, .324, .123 , .543, .00054
.2243, .333, .53343 , .4434

现在,我想使用 GPU 获取每个数字出现的次数。我相信这在 GPU 上会比在 CPU 上更快,因为每个线程都可以处理一个列表。我应该在 GPU 上使用什么数据结构来轻松获得上述计数。例如,对于上述情况,答案将如下所示:

.333 = 2 times in entire file
.324 = 1 time

等等。

我正在寻找一个通用的解决方案。不是仅适用于具有特定计算能力的设备

只需编写 Pavan 建议的内核,看看我是否有效地实现了它:

int uniqueEle = newend.valiter – d_A;

int* count;
cudaMalloc((void**)&count, uniqueEle * sizeof(int)); // stores the count of each unique element
int TPB = 256;
int blocks = uniqueEle + TPB -1 / TPB;
//Cast d_I to raw pointer called d_rawI
launch<<<blocks,TPB>>>(d_rawI,count,uniqueEle);

__global__ void launch(int *i, int* count, int n){
    int id = blockDim.x * blockIdx.x + threadIdx.x;
    __shared__ int indexes[256];
    if(id < n ){
        indexes[threadIdx.x] = i[id];
        //as occurs between two blocks
        if(id % 255 == 0){
            count[indexes] = i[id+1] - i[id];
        }
    }
    __syncthreads();
    if(id < ele - 1){
        if(threadIdx.x < 255)
            count[id] = indexes[threadIdx.x+1] – indexes[threadIdx.x];

    }
}

问题:如何修改此内核以使其处理任意大小的数组。即处理线程总数时的情况

【问题讨论】:

  • 您的号码列表大约有多长?它们都是 32 位浮点数吗?
  • @programmer:你认为内核代码到底是做什么的(撇开它不会编译的事实不谈)?
  • @talonmies:它计算两个连续数组索引的值之间的差异
  • @Programmer:那个“代码”中有一些非常荒谬的东西。看看我的回答,可能是你想要做的事情。
  • @Programmer,把我在聊天中提供的编辑后的代码贴出来可能会很好。

标签: cuda parallel-processing gpu gpgpu


【解决方案1】:

这是我在matlab中的代码

A = [333, .324, .123 , .543, .00054 .2243, .333, .53343 , .4434];
[values, locations] = unique(A);   % Find unique values and their locations
counts = diff([0, locations]);     % Find the count based on their locations

在纯 cuda 中没有简单的方法可以做到这一点,但您可以使用现有的库来做到这一点。

1)Thrust

它还随 CUDA 4.0 的 CUDA 工具包一起提供。

matlab代码可以通过以下函数粗略翻译成推力。我对推力不太精通,但我只是想让您了解一下要查看的例程。

float _A[] = {.333, .324, .123 , .543, .00054 .2243, .333, .53343 , .4434};
int _I[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
float *A, *I; 
// Allocate memory on device and cudaMempCpy values from _A to A and _I to I
int num = 9;
// Values vector
thrust::device_vector<float>d_A(A, A+num);
// Need to sort to get same values together    
thrust::stable_sort(d_A, d_A+num);
// Vector containing 0 to num-1
thrust::device_vector<int>d_I(I, I+num);
// Find unique values and elements
thrust::device_vector<float>d_Values(num), d_Locations(num), d_counts(num);
// Find unique elements
thrust::device_vector<float>::iterator valiter;
thrust::device_vector<int>::iterator idxiter;
thrust::pair<valiter, idxiter> new_end;
new_end = thrust::unique_by_key(d_A, d_A+num, d_I, d_Values, d_Locations);

您现在有了每个唯一值的第一个实例的位置。您现在可以启动内核来查找 d_Locations 中从 0 到 new_end 的相邻元素之间的差异。从 num 中减去最终值以获得最终位置的计数。

编辑(添加通过聊天提供的代码)

这里是差异代码需要如何完成

#define MAX_BLOCKS 65535
#define roundup(A, B) = (((A) + (B) - 1) / (B))

int uniqueEle = newend.valiter – d_A;
int* count;
cudaMalloc((void**)&count, uniqueEle * sizeof(int));

int TPB = 256;
int num_blocks = roundup(uniqueEle, TPB);
int blocks_y = roundup(num_blocks, MAX_BLOCKS);
int blocks_x = roundup(num_blocks, blocks_y);
dim3 blocks(blocks_x, blocks_y);

kernel<<<blocks,TPB>>>(d_rawI, count, uniqueEle);

__global__ void kernel(float *i, int* count, int n)
{
int tx = threadIdx.x;
int bid = blockIdx.y * gridDim.x + blockIdx.x;
int id = blockDim.x * bid + tx;
__shared__ int indexes[256];

if (id < n) indexes[tx] = i[id];
__syncthreads();

if (id < n - 1) {
if (tx < 255) count[id] = indexes[tx + 1] - indexes[tx];
else count[id] = i[id + 1] - indexes[tx];
}

if (id == n - 1) count[id] = n - indexes[tx];
return;
}

2) ArrayFire

这是一个易于使用、免费基于数组的库。

您可以在 ArrayFire 中执行以下操作。

using namespace af;
float h_A[] = {.333, .324, .123 , .543, .00054 .2243, .333, .53343 , .4434};
int num = 9;
// Transfer data to device
array A(9, 1, h_A);
array values, locations, original;
// Find the unique values and locations
setunique(values, locations, original, A);
// Locations are 0 based, add 1.
// Add *num* at the end to find count of last value. 
array counts = diff1(join(locations + 1, num));

披露:我为开发此软件的 AccelerEyes 工作。

【讨论】:

  • 但在这种情况下,您假设您知道所有唯一值。如果您不知道所有唯一值怎么办?
  • +程序员,算法在寻找你最终结果的过程中寻找唯一值。它并不期望一开始就具有独特的价值。
  • 非常感谢。只是好奇:上述推力函数是否依赖于原子运算符?
  • 程序员,我们已经编写了功能等同于thrust 的函数。我认为它们中的任何一个都不需要原子操作。
  • 不,我指的是像 unique_key 这样的原始推力函数。这是否使用原子运算符
【解决方案2】:

这里有一个解决方案:

__global__ void counter(float* a, int* b, int N)
{
    int idx = blockIdx.x*blockDim.x+threadIdx.x;

    if(idx < N)
    {
        float my = a[idx];
        int count = 0;
        for(int i=0; i < N; i++)
        {
            if(my == a[i])
                count++;
        }

        b[idx]=count;
    }
}

int main()
{

    int threads = 9;
    int blocks = 1;
    int N = blocks*threads;
    float* h_a;
    int* h_b;
    float* d_a;
    int* d_b;

    h_a = (float*)malloc(N*sizeof(float));
    h_b = (int*)malloc(N*sizeof(int));

    cudaMalloc((void**)&d_a,N*sizeof(float));
    cudaMalloc((void**)&d_b,N*sizeof(int));

    h_a[0]= .333f; 
    h_a[1]= .324f;
    h_a[2]= .123f;
    h_a[3]= .543f;
    h_a[4]= .00054f;
    h_a[5]= .2243f;
    h_a[6]= .333f;
    h_a[7]= .53343f;
    h_a[8]= .4434f;

    cudaMemcpy(d_a,h_a,N*sizeof(float),cudaMemcpyHostToDevice);

    counter<<<blocks,threads>>>(d_a,d_b,N);

    cudaMemcpy(h_b,d_b,N*sizeof(int),cudaMemcpyDeviceToHost);

    for(int i=0; i < N; i++)
    {
        printf("%f = %d times\n",h_a[i],h_b[i]);
    }

    cudaFree(d_a);
    cudaFree(d_b);
    free(h_a);
    free(h_b);
    getchar();
    return 0;
}

【讨论】:

  • 您打印 .333f 两次,这是错误的。其次,您只使用了 1 个块。此外,您如何确定唯一元素的数量???您刚开始 CUDA 编程吗?
  • 根据您的问题:“我想使用 GPU 获取每个数字出现的次数”。我将突出显示“每个数字”这句话。这正是你所要求的。 1 块仅归因于您的示例,其中您总共有 9 个元素。
  • 您认为您的解决方案是否比 Pavan 的解决方案更高效?
  • Brano,每个线程都有一个大小为 N 的 for 循环?!你是否意识到这对于 CUDA 编程来说是多么可笑?
  • 也许这是一个荒谬的解决方案,但至少我是唯一一个给他一个纯 CUDA 实现的人。我的解决方案仍然是可修改的,并且不依赖于您无法控制的现有库。人们还需要意识到,thrust 和 ArrayFire 都是包装器,它们封装了程序员看不到的大量 API 调用,并可能导致混淆,例如多个内核调用,更糟糕的是,alloc 和 free 将对性能产生巨大影响你可以重复使用内存。
【解决方案3】:

要回答这个问题的最新附录 - 将完成 thrust 方法 proposed by Pavan 的差异内核可能看起来像这样:

template<int blcksz>
__global__ void diffkernel(const int *i, int* count, const int n) { 
    int id = blockDim.x * blockIdx.x + threadIdx.x; 
    int strd = blockDim.x * gridDim.x;
    int nmax = blcksz * ((n/blcksz) + ((n%blcksz>0) ? 1 : 0));

    __shared__ int indices[blcksz+1]; 

    for(; id<nmax; id+=strd) {
        // Data load
        indices[threadIdx.x] = (id < n) ? i[id] : n; 
        if (threadIdx.x == (blcksz-1)) 
            indices[blcksz] = ((id+1) < n) ? i[id+1] : n; 

        __syncthreads(); 

        // Differencing calculation
        int diff = indices[threadIdx.x+1] - indices[threadIdx.x];

        // Store
        if (id < n) count[id] = diff;

        __syncthreads(); 
    }
} 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-26
    • 2023-02-08
    • 2012-05-07
    • 2020-02-27
    • 1970-01-01
    • 2010-11-14
    相关资源
    最近更新 更多