簡體   English   中英

在 CUDA 中獲取多個數組的唯一元素

[英]Get unique elements of multiple arrays in CUDA

問題在於:數組的數量,例如,2000 個數組,但每個數組中只有 256 個整數。 並且整數的范圍相當可觀,例如 [0, 1000000]。

我想獲取每個數組的唯一元素,換句話說,刪除重復的元素。 我有兩個解決方案:

  1. 使用 Thrust 獲取每個數組的唯一元素,因此我必須執行 2000 次thrust::unique 但是每個數組都很小,這種方式可能得不到很好的性能。

  2. 在 cuda 內核中實現哈希表,使用 2000 個塊,每個塊中使用 256 個線程。 並利用共享內存來實現哈希表,那么每個塊都會產生一個元素唯一的數組。

以上兩種方法看起來不專業,請問CUDA有沒有優雅的方法可以解決問題?

如果您像在此問題中所做的那樣修改數據,則可以使用thrust::uniqueSegmented Sort with CUDPP/Thrust

為簡單per_array ,我們假設每個數組包含per_array元素,並且總共有array_num數組。 每個元素都在[0,max_element]范圍內。

具有per_array=4array_num=3max_element=2演示data可能如下所示:

data = {1,0,1,2},{2,2,0,0},{0,0,0,0}

為了表示每個元素對相應數組的成員資格,我們使用以下flags

flags = {0,0,0,0},{1 1 1 1},{2,2,2,2}

為了獲得分割數據集的每個數組的唯一元素,我們需要執行以下步驟:

  1. 轉換data使每個數組i的元素都在唯一的范圍內[i*2*max_element,i*2*max_element+max_element]

     data = data + flags*2*max_element data = {1,0,1,2},{6,6,4,4},{8,8,8,8}
  2. 對轉換后的數據進行排序:

     data = {0,0,1,2},{4,4,6,6},{8,8,8,8}
  3. 使用data作為鍵和flags作為值來應用thrust::unique_by_key

     data = {0,1,2}{4,6}{8} flags = {0,0,0}{1,1}{2}
  4. data轉換回原始值:

     data = data - flags*2*max_element data = {0,1,2}{0,2}{0}

max_element的最大值受用於表示data的整數大小的限制。 如果它是一個n位的無符號整數:

max_max_element(n,array_num) = 2^n/(2*(array_num-1)+1)

鑒於您的array_num=2000 ,您將獲得 32 位和 64 位無符號整數的以下限制:

max_max_element(32,2000) = 1074010
max_max_element(64,2000) = 4612839228234447

以下代碼實現了上述步驟:

unique_per_array.cu

#include <thrust/device_vector.h>
#include <thrust/extrema.h>
#include <thrust/transform.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/functional.h>
#include <thrust/sort.h>
#include <thrust/unique.h>
#include <thrust/copy.h>

#include <iostream>
#include <cstdint>

#define PRINTER(name) print(#name, (name))
template <template <typename...> class V, typename T, typename ...Args>
void print(const char* name, const V<T,Args...> & v)
{
    std::cout << name << ":\t";
    thrust::copy(v.begin(), v.end(), std::ostream_iterator<T>(std::cout, "\t"));
    std::cout << std::endl;
}

int main()
{ 
    typedef uint32_t Integer;

    const std::size_t per_array = 4;
    const std::size_t array_num = 3;

    const std::size_t total_count = array_num * per_array;

    Integer demo_data[] = {1,0,1,2,2,2,0,0,0,0,0,0};

    thrust::device_vector<Integer> data(demo_data, demo_data+total_count);    

    PRINTER(data);

    // if max_element is known for your problem,
    // you don't need the following operation 
    Integer max_element = *(thrust::max_element(data.begin(), data.end()));
    std::cout << "max_element=" << max_element << std::endl;

    using namespace thrust::placeholders;

    // create the flags

    // could be a smaller integer type as well
    thrust::device_vector<uint32_t> flags(total_count);

    thrust::counting_iterator<uint32_t> flags_cit(0);

    thrust::transform(flags_cit,
                      flags_cit + total_count,
                      flags.begin(),
                      _1 / per_array);
    PRINTER(flags);


    // 1. transform data into unique ranges  
    thrust::transform(data.begin(),
                      data.end(),
                      thrust::counting_iterator<Integer>(0),
                      data.begin(),
                      _1 + (_2/per_array)*2*max_element);
    PRINTER(data);

    // 2. sort the transformed data
    thrust::sort(data.begin(), data.end());
    PRINTER(data);

    // 3. eliminate duplicates per array
    auto new_end = thrust::unique_by_key(data.begin(),
                                         data.end(),
                                         flags.begin());

    uint32_t new_size = new_end.first - data.begin();
    data.resize(new_size);
    flags.resize(new_size);

    PRINTER(data);
    PRINTER(flags);

    // 4. transform data back
    thrust::transform(data.begin(),
                      data.end(),
                      flags.begin(),
                      data.begin(),
                      _1 - _2*2*max_element);

    PRINTER(data);

}    

編譯和運行產量:

$ nvcc -std=c++11 unique_per_array.cu -o unique_per_array && ./unique_per_array

data:   1   0   1   2   2   2   0   0   0   0   0   0   
max_element=2
flags:  0   0   0   0   1   1   1   1   2   2   2   2   
data:   1   0   1   2   6   6   4   4   8   8   8   8   
data:   0   1   1   2   4   4   6   6   8   8   8   8   
data:   0   1   2   4   6   8   
flags:  0   0   0   1   1   2   
data:   0   1   2   0   2   0   

還有一件事:

推力開發版本中,對thrust::unique*進行了改進,將性能提高了大約 25% 如果您的目標是更好的性能,您可能想嘗試這個版本。

我認為推力::unique_copy()可以幫助您做到這一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM