CUDA或推力:在分组数组中获得n个最大数

CUDA或推力:在分组数组中获得n个最大数,cuda,thrust,Cuda,Thrust,我把一个数组分成n个组,每个组大小相等,我想得到每个组中的n个最大数 例如: 我计算每个线程的groupID,并在全局数组上使用atomicMax。因此,当组数较少时,性能较差。似乎推力::按_键减少可以做到这一点,但我还没有弄清楚 有更好的想法吗?一种可能的方法是使用。至少在推力方面,我认为没有更好的方法 下面是一个成功的例子: $ cat t663.cu #include <iostream> #include <thrust/reduce.h> #include &

我把一个数组分成n个组,每个组大小相等,我想得到每个组中的n个最大数

例如:

我计算每个线程的groupID,并在全局数组上使用atomicMax。因此,当组数较少时,性能较差。似乎<代码>推力::按_键减少可以做到这一点,但我还没有弄清楚


有更好的想法吗?

一种可能的方法是使用。至少在推力方面,我认为没有更好的方法

下面是一个成功的例子:

$ cat t663.cu
#include <iostream>
#include <thrust/reduce.h>
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <thrust/functional.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/fill.h>
#include <stdlib.h>

#define N_GROUPS 4
#define GRP_SIZE 4
#define nTPB 256
#define DSIZE (N_GROUPS * GRP_SIZE)
#define SCALE_MOD 1024
#include <time.h>
#include <sys/time.h>

unsigned long long dtime_usec(unsigned long long prev){
#define USECPSEC 1000000ULL
  timeval tv1;
  gettimeofday(&tv1,0);
  return ((tv1.tv_sec * USECPSEC)+tv1.tv_usec) - prev;
}

template <typename T>
__global__ void max_kernel(const T *data, T *result, const int num_groups, const int group_size){

  int idx = threadIdx.x+blockDim.x*blockIdx.x;

  if (idx < (num_groups*group_size))
    atomicMax(result + (idx/group_size), data[idx]);
}


struct idx_fctr : public thrust::unary_function<int, int>
{
  int mod;
  idx_fctr(const int _mod) : mod(_mod) {} ;
  __host__ __device__ int operator()(const int &val) const {
    return val/mod;
  }
};

template <typename T>
struct max_fctr : public thrust::binary_function<T, T, T>
{
  __host__ __device__ T operator()(const T &val1, const T &val2) const {
  return (val1>val2)?val1:val2;
  }
};

int main(int argc, char *argv[]){
  int grp_size = GRP_SIZE;
  int n_groups = N_GROUPS;
  if (argc > 2){
    grp_size = atoi(argv[1]);
    n_groups = atoi(argv[2]);}

  int *data;
  int dsize = grp_size*n_groups;
  data = new int[dsize];

  for (int i = 0; i < dsize; i++) data[i] = rand()%SCALE_MOD;
  thrust::device_vector<int> d_data(data, data+dsize);
  thrust::device_vector<int> d_keys_out(n_groups);
  thrust::device_vector<int> d_vals_out(n_groups);

  unsigned long long dtime = dtime_usec(0);
  thrust::reduce_by_key(thrust::make_transform_iterator(thrust::make_counting_iterator(0), idx_fctr(grp_size)), thrust::make_transform_iterator(thrust::make_counting_iterator(dsize), idx_fctr(grp_size)), d_data.begin(), d_keys_out.begin(), d_vals_out.begin(), thrust::equal_to<int>(), max_fctr<int>());
  cudaDeviceSynchronize();
  dtime = dtime_usec(dtime);
  std::cout << "thrust time: " << dtime/(float)USECPSEC << std::endl;
#ifdef DEBUG_PRINT
  std::cout << "data: " << std::endl;
  thrust::copy(d_data.begin(), d_data.end(), std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl << "keys: " << std::endl;
  thrust::copy(d_keys_out.begin(), d_keys_out.end(), std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl << "maxima: " << std::endl;
  thrust::copy(d_vals_out.begin(), d_vals_out.end(), std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl;
#endif

  thrust::fill(d_vals_out.begin(), d_vals_out.end(), 0);
  dtime = dtime_usec(0);
  max_kernel<<<(dsize+nTPB-1)/nTPB, nTPB>>>(thrust::raw_pointer_cast(d_data.data()), thrust::raw_pointer_cast(d_vals_out.data()), n_groups, grp_size);
  cudaDeviceSynchronize();
  dtime = dtime_usec(dtime);
  std::cout << "kernel time: " << dtime/(float)USECPSEC << std::endl;
#ifdef DEBUG_PRINT
  std::cout << "data: " << std::endl;
  thrust::copy(d_data.begin(), d_data.end(), std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl << "keys: " << std::endl;
  thrust::copy(d_keys_out.begin(), d_keys_out.end(), std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl << "maxima: " << std::endl;
  thrust::copy(d_vals_out.begin(), d_vals_out.end(), std::ostream_iterator<int>(std::cout, ","));
  std::cout << std::endl;
#endif

  return 0;
}  


$ nvcc -arch=sm_20 -o t663 t663.cu
$ ./t663
data:
359,966,105,115,81,255,74,236,809,205,186,939,498,763,483,326,
keys:
0,1,2,3,
maxima:
966,255,939,763,
$
$cat t663.cu
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义N_组4
#定义GRP_尺寸4
#定义nTPB 256
#定义DSIZE(N_组*GRP_大小)
#定义比例_mod1024
#包括
#包括
无符号long-long-dtime\u-usec(无符号long-long-prev){
#定义USECPSEC 10000000ull
timeval-tv1;
gettimeofday(&tv1,0);
返回((tv1.tv_sec*USECPSEC)+tv1.tv_usec)-prev;
}
模板
__全局无效最大内核(常量T*数据、T*结果、常量int num\u组、常量int组大小){
int idx=threadIdx.x+blockDim.x*blockIdx.x;
如果(idx<(组数*组大小))
atomicMax(结果+(idx/组大小),数据[idx]);
}
结构idx_fctr:公共推力::一元函数
{
整数模;
idx_fctr(const int_mod):mod(_mod){};
__主机\uuuuu\uuuu设备\uuuuu int运算符()(常量int&val)常量{
返回val/mod;
}
};
模板
struct max_fctr:公共推力::二进制函数
{
__主机\uuuuu\uuuu设备\uuuuu操作器()(常数T&val1,常数T&val2)常数{
返回(val1>val2)?val1:val2;
}
};
int main(int argc,char*argv[]){
int grp_size=grp_size;
int n_组=n_组;
如果(argc>2){
grp_size=atoi(argv[1]);
n_群=atoi(argv[2]);}
int*数据;
int dsize=grp_大小*n_组;
数据=新整数[dsize];
对于(int i=0;istd::cout
reduce\u by\u key
可以。你是在问如何使用
reduce\u by\u key
吗?@RobertCrovella是的,
reduce\u by\u key
可以,但我认为
reduce\u by\u key
可以先对键排序,在我的情况下,这是不必要的,因为我的情况下键是连续的。我只知道我可以对每个g使用atomicMax分组现在要获得最大数量,必须有更快的方法。你能给我一些提示吗?
reduce\u by\u key
不会排序任何东西。你传递给reduce\u by\u key的密钥必须已经分组。非常感谢,我应该更仔细地阅读
reduce\u by\u key
的描述,确实,在之前必须对密钥进行分组使用
reduce\u by\u key
。我被一个例子误导了。我尝试了
reduce\u by\u key
,但性能比直接在全局内存上使用atomicMax差。这很奇怪,我添加了
推力::cuda::par(推力缓存定位器)
,性能比以前更好,但比atomicMax更差。是的,我同意性能会根据数据集的确切大小、组大小以及GPU类型而变化。您没有指出任何这些内容。我已更新代码以提供基准比较。