计算CUDA数组中数字的出现次数

计算CUDA数组中数字的出现次数,cuda,thrust,Cuda,Thrust,我使用CUDA在GPU上存储了一个无符号整数数组(通常1000000元素)。我想计算数组中每个数字的出现次数。只有几个不同的数字(大约10),但这些数字的范围可以从1到1000000。大约9/10th的数字是0,我不需要他们的计数。结果如下所示: 58458 -> 1000 occurrences 15 -> 412 occurrences 我有一个使用atomicAdds的实现,但是它太慢了(很多线程都写到同一个地址)。有人知道快速/高效的方法吗?我想你可以在CUDA示例中找到帮

我使用CUDA在GPU上存储了一个无符号整数数组(通常
1000000
元素)。我想计算数组中每个数字的出现次数。只有几个不同的数字(大约
10
),但这些数字的范围可以从1到
1000000
。大约
9/10
th的数字是
0
,我不需要他们的计数。结果如下所示:

58458 -> 1000 occurrences
15 -> 412 occurrences

我有一个使用
atomicAdd
s的实现,但是它太慢了(很多线程都写到同一个地址)。有人知道快速/高效的方法吗?

我想你可以在CUDA示例中找到帮助,特别是直方图示例。它们是GPU计算SDK的一部分。
你可以在这里找到它。他们甚至有一份解释算法的白皮书。

您可以通过首先对数字进行排序,然后进行键控缩减来实现直方图


最简单的方法是使用
推力::排序
,然后使用
推力::按_键减少
。它通常也比基于原子的临时分类快得多。这是一个。

我正在比较重复问题中建议的两种方法,即

  • 使用
    推力::计数迭代器
    推力::上限
    ,遵循直方图推力示例
  • 使用
    asch::unique\u copy
    asch::upper\u bound
  • 下面,请找到一个完整的例子

    #include <time.h>       // --- time
    #include <stdlib.h>     // --- srand, rand
    #include <iostream>
    
    #include <thrust\host_vector.h>
    #include <thrust\device_vector.h>
    #include <thrust\sort.h>
    #include <thrust\iterator\zip_iterator.h>
    #include <thrust\unique.h>
    #include <thrust/binary_search.h>
    #include <thrust\adjacent_difference.h>
    
    #include "Utilities.cuh"
    #include "TimingGPU.cuh"
    
    //#define VERBOSE
    #define NO_HISTOGRAM
    
    /********/
    /* MAIN */
    /********/
    int main() {
    
        const int N = 1048576;
        //const int N = 20;
        //const int N = 128;
    
        TimingGPU timerGPU;
    
        // --- Initialize random seed
        srand(time(NULL));
    
        thrust::host_vector<int> h_code(N);
    
        for (int k = 0; k < N; k++) {
            // --- Generate random numbers between 0 and 9
            h_code[k] = (rand() % 10);
        }
    
        thrust::device_vector<int> d_code(h_code);
        //thrust::device_vector<unsigned int> d_counting(N);
    
        thrust::sort(d_code.begin(), d_code.end());
    
        h_code = d_code;
    
        timerGPU.StartCounter();
    
    #ifdef NO_HISTOGRAM
        // --- The number of d_cumsum bins is equal to the maximum value plus one
        int num_bins = d_code.back() + 1;
    
        thrust::device_vector<int> d_code_unique(num_bins);
        thrust::unique_copy(d_code.begin(), d_code.end(), d_code_unique.begin());
        thrust::device_vector<int> d_counting(num_bins);
        thrust::upper_bound(d_code.begin(), d_code.end(), d_code_unique.begin(), d_code_unique.end(), d_counting.begin());  
    #else
        thrust::device_vector<int> d_cumsum;
    
        // --- The number of d_cumsum bins is equal to the maximum value plus one
        int num_bins = d_code.back() + 1;
    
        // --- Resize d_cumsum storage
        d_cumsum.resize(num_bins);
    
        // --- Find the end of each bin of values - Cumulative d_cumsum
        thrust::counting_iterator<int> search_begin(0);
        thrust::upper_bound(d_code.begin(), d_code.end(), search_begin, search_begin + num_bins, d_cumsum.begin());
    
        // --- Compute the histogram by taking differences of the cumulative d_cumsum
        //thrust::device_vector<int> d_counting(num_bins);
        //thrust::adjacent_difference(d_cumsum.begin(), d_cumsum.end(), d_counting.begin());
    #endif
    
        printf("Timing GPU = %f\n", timerGPU.GetCounter());
    
    #ifdef VERBOSE
        thrust::host_vector<int> h_counting(d_counting);
        printf("After\n");
        for (int k = 0; k < N; k++) printf("code = %i\n", h_code[k]);
    #ifndef NO_HISTOGRAM
        thrust::host_vector<int> h_cumsum(d_cumsum);
        printf("\nCounting\n");
        for (int k = 0; k < num_bins; k++) printf("element = %i; counting = %i; cumsum = %i\n", k, h_counting[k], h_cumsum[k]);
    #else
        thrust::host_vector<int> h_code_unique(d_code_unique);
    
        printf("\nCounting\n");
        for (int k = 0; k < N; k++) printf("element = %i; counting = %i\n", h_code_unique[k], h_counting[k]);
    #endif
    #endif
    }
    

    请注意,不需要严格地显式计算相邻差,因为如果需要,此操作可以在内核处理过程中手动完成。

    正如其他人所说,您可以使用
    按键排序和减少方法来计数频率。在我的例子中,我需要获得阵列的模式(最大频率/发生次数),因此我的解决方案如下:

    1-首先,我们创建两个新数组,一个包含输入数据的副本,另一个填充输入数据副本,以便稍后减少它(总和):

    3-稍后,我们为(唯一)键及其频率创建两个输出向量:

    thrust::device_vector<int> output_keys(N);
    thrust::device_vector<int> output_freqs(N);
    
    推力::设备向量输出键(N);
    推力:装置矢量输出频率(N);
    
    4-最后,我们按键执行缩减:

    // Reduce contiguous keys: [1 3 3 3 2 2 3] => [1 3 2 1] Vs. [1 3 3 3 3 2 2] => [1 4 2] 
    thrust::pair<thrust::device_vector<int>::iterator, thrust::device_vector<int>::iterator> new_end;
    new_end = thrust::reduce_by_key(dev_keys.begin(), dev_keys.end(), dev_ones.begin(), output_keys.begin(), output_freqs.begin());
    
    //减少连续键:[1 3 3 2 3]=>[1 3 2 1]与[1 3 3 3 2 2]=>[1 4 2]
    推力:配对新的_端;
    new_end=推力::按键减少键(dev_keys.begin(),dev_keys.end(),dev_ones.begin(),output_keys.begin(),output_freqs.begin());
    
    5-…如果我们愿意,我们可以得到最频繁的元素

    // Get most frequent element
    // Get index of the maximum frequency
    int num_keys = new_end.first  - output_keys.begin();
    thrust::device_vector<int>::iterator iter = thrust::max_element(output_freqs.begin(), output_freqs.begin() + num_keys);
    unsigned int index = iter - output_freqs.begin();
    
    int most_frequent_key = output_keys[index];
    int most_frequent_val = output_freqs[index];  // Frequencies
    
    //获取最频繁的元素
    //获取最大频率的索引
    int num_keys=new_end.first-output_keys.begin();
    推力::设备\向量::迭代器iter=推力::最大\元素(输出\频率.开始(),输出\频率.开始()+数字\键);
    unsigned int index=iter-output_freqs.begin();
    int最频繁的键=输出键[索引];
    int most_frequency_val=输出频率[索引];//频率
    
    这就是我要找的。非常感谢。
    thrust::device_vector<int> output_keys(N);
    thrust::device_vector<int> output_freqs(N);
    
    // Reduce contiguous keys: [1 3 3 3 2 2 3] => [1 3 2 1] Vs. [1 3 3 3 3 2 2] => [1 4 2] 
    thrust::pair<thrust::device_vector<int>::iterator, thrust::device_vector<int>::iterator> new_end;
    new_end = thrust::reduce_by_key(dev_keys.begin(), dev_keys.end(), dev_ones.begin(), output_keys.begin(), output_freqs.begin());
    
    // Get most frequent element
    // Get index of the maximum frequency
    int num_keys = new_end.first  - output_keys.begin();
    thrust::device_vector<int>::iterator iter = thrust::max_element(output_freqs.begin(), output_freqs.begin() + num_keys);
    unsigned int index = iter - output_freqs.begin();
    
    int most_frequent_key = output_keys[index];
    int most_frequent_val = output_freqs[index];  // Frequencies