Sorting CUDA推力和按键排序

Sorting CUDA推力和按键排序,sorting,cuda,permutation,thrust,Sorting,Cuda,Permutation,Thrust,我正在寻找一种CUDA上的排序算法,它可以对元素数组a(double)进行排序,并为该数组a返回一个键数组B。 我知道推力库中的sort\u by_key函数,但我希望我的元素数组A保持不变。 我能做什么 我的代码是: void sortCUDA(double V[], int P[], int N) { real_t *Vcpy = (double*) malloc(N*sizeof(double)); memcpy(Vcpy,V,N*sizeof(doubl

我正在寻找一种CUDA上的排序算法,它可以对元素数组a(double)进行排序,并为该数组a返回一个键数组B。 我知道推力库中的
sort\u by_key
函数,但我希望我的元素数组A保持不变。 我能做什么

我的代码是:

void sortCUDA(double V[], int P[], int N) {

        real_t *Vcpy = (double*) malloc(N*sizeof(double));
        memcpy(Vcpy,V,N*sizeof(double));

        thrust::sort_by_key(V, V + N, P);
        free(Vcpy);
}
我将推力算法与顺序cpu上的其他算法进行比较

N               mergesort       sortCUDA
113             0.000008        0.000010
226             0.000018        0.000016
452             0.000036        0.000020
905             0.000061        0.000034
1810            0.000135        0.000071
3621            0.000297        0.000156
7242            0.000917        0.000338
14484           0.001421        0.000853
28968           0.003069        0.001931
57937           0.006666        0.003939
115874          0.014435        0.008025
231749          0.031059        0.016718
463499          0.067407        0.039848
926999          0.148170        0.118003
1853998         0.329005        0.260837
3707996         0.731768        0.544357
7415992         1.638445        1.073755
14831984        3.668039        2.150179
115035495       39.276560       19.812200
230070990       87.750377       39.762915
460141980       200.940501      74.605219
推力性能还不错,但我认为如果我使用OMP,可能很容易获得更好的CPU时间

我想这是因为对memcpy来说

解决方案:

void thrustSort(double V[], int P[], int N)
{
        thrust::device_vector<int> d_P(N);
        thrust::device_vector<double> d_V(V, V + N);
        thrust::sequence(d_P.begin(), d_P.end());

        thrust::sort_by_key(d_V.begin(), d_V.end(), d_P.begin());

        thrust::copy(d_P.begin(),d_P.end(),P);
}
void排序(双V[],int P[],int N)
{
推力:装置矢量dp(N);
推力:装置矢量d(V,V+N);
推力:序列(dup.begin(),dup.end());
推力::按键排序(d_V.begin(),d_V.end(),d_P.begin());
推力::复制(dup.begin(),dup.end(),P);
}

其中V是一个我的双值排序

这个数组有多大?就速度而言,最有效的方法可能是在排序之前复制原始数组(如果内存可用)。

您可以修改比较运算符对键而不是值进行排序@Robert Crovella正确地指出,无法从主机分配原始设备指针。修改后的算法如下:

struct cmp : public binary_function<int,int,bool>
{
  cmp(const double *ptr) : rawA(ptr) { }

  __host__ __device__ bool operator()(const int i, const int j) const 
  {return rawA[i] > rawA[j];}

   const double *rawA; // an array in global mem
}; 

void sortkeys(double *A, int n) {
  // move data to the gpu
  thrust::device_vector<double> devA(A, A + n);
  double *rawA = thrust::raw_pointer_cast(devA.data());

  thrust::device_vector<int> B(n);
  // initialize keys
  thrust::sequence(B.begin(), B.end());
  thrust::sort(B.begin(), B.end(), cmp(rawA));
  // B now contains the sorted keys
 }
struct-cmp:公共二进制函数
{
cmp(const double*ptr):rawA(ptr){}
__主机设备布尔运算符()(常量int i,常量int j)常量
{return rawA[i]>rawA[j];}
const double*rawA;//全局内存中的数组
}; 
无效排序键(双*A,整数n){
//将数据移动到gpu
推力:装置矢量devA(A,A+n);
double*rawA=thrust::raw\u pointer\u cast(devA.data());
推力:装置_矢量B(n);
//初始化密钥
推力:顺序(B.开始(),B.结束());
推力::排序(B.begin(),B.end(),cmp(rawA));
//B现在包含已排序的键
}
这是arrayfire的替代方案。尽管我不确定哪一个更有效,因为ArrayFile解决方案使用了两个额外的数组:

void sortkeys(double *A, int n) {
   af::array devA(n, A, af::afHost);
   af::array vals, indices;
   // sort and populate vals/indices arrays
   af::sort(vals, indices, devA);
   std::cout << devA << "\n" << indices << "\n";
}
void排序键(双*A,整数n){
af::array devA(n,A,af::afHost);
af::数组VAL,索引;
//排序和填充VAL/索引数组
af::排序(VAL、索引、devA);

std::cout基于@asm提供的答案(我无法让它工作),这段代码似乎对我有效,并且只对键进行排序。但是,我相信它仅限于键按顺序0、1、2、3、4…对应于(双)值的情况。因为这是一个“索引值”排序,它可以扩展到任意键序列的情况,可能是通过执行索引复制。但是我不确定生成索引序列然后重新排列原始键的过程是否比仅将原始值数据复制到新向量(对于任意键的情况)快

#包括
#包括
#包括
#包括
使用名称空间std;
__device_uuu双倍*rawA;//全局内存中的数组
结构cmp:公共二进制函数
{
__主机设备布尔运算符()(常量int i,常量int j)常量
{return(rawA[i]对于(It i=0;在排序前复制A的副本),如果你是一个推力用户,你可能想考虑加入。是的,我确实这么做了,但是性能大大降低了,也许你应该发布一些代码并回答关于大小的问题。我希望分拣操作的成本要比矢量拷贝的成本高很多。我需要了解更多信息,也许看看。向量可以存在于主机或设备上。如果你传递基于主机的向量(或指向数组的指针),推力将使用基于主机的算法进行排序(让GPU闲置)。如果你传递基于设备的向量或指针,推力将使用基于设备的算法进行排序(即,在GPU上)。您发布的代码给我的印象是,您的指针是基于主机的。事实上,我印象深刻的是,推力比您的mergesort更快,即使大小只有226,特别是因为您正在增加向量副本的成本(不知道您是否使用mergesort进行此操作——您没有发布此代码)如果您使用推力装置排序,将向量复制到该装置将产生成本。这将惩罚您的小尺寸排序,但可能会大大改进大尺寸排序。此外,排序速度应该会大大加快。这是我第一次做的,但速度太慢了。我很难让它正常工作。Bu除此之外,如果键不是序列(0,1,2,…),这会起作用吗?大概sort_by_key的一般情况不需要这样的键。此外,我不认为这行代码正在做您希望它做的事情:
rawA=推力::raw_pointer_cast(devA.data())
我无法让它工作。它确实可以编译,但如果在那一行之后尝试取消对rawA的引用,则会引发一个异常。我可以使用基本相同的方法获得一个备用版本,但不是使用那一行。嗯,你是对的,在主机上分配一个原始设备指针没有多大意义。。但感谢您提供了一个工作示例。我不确定您所说的键不是序列(0,1,2,…)?具有序列[0,1,2,…n]您可以提供到任何其他键序列的一对一映射。常规推力排序按键不要求键为0、1、2。请看一个示例。您的方法在使用t的示例中不起作用
#include <iostream>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/sort.h>

using namespace std;

__device__  double *rawA; // an array in global mem

struct cmp : public binary_function<int, int, bool>
{
  __host__ __device__  bool operator()(const int i, const int j) const
  {return ( rawA[i] < rawA[j]);}
};

void sortkeys(double *A, int n) {
  // move data to the gpu
  thrust::device_vector<double> devA(A, A + n);
//  rawA = thrust::raw_pointer_cast(&(devA[0]));
  double *test = raw_pointer_cast(devA.data());
  cudaMemcpyToSymbol(rawA, &test, sizeof(double *));

  thrust::device_vector<int> B(n);
  // initialize keys
  thrust::sequence(B.begin(), B.end());
  thrust::sort(B.begin(), B.end(), cmp());
  // B now contains the sorted keys
  thrust::host_vector<int> hostB = B;
  for (int i=0; i<hostB.size(); i++)
    std::cout << hostB[i] << " ";
  std::cout<<std::endl;
  for (int i=0; i<hostB.size(); i++)
    std::cout << A[hostB[i]] << " ";
  std::cout<<std::endl;
 }


int main(){

  double C[] = {0.7, 0.3, 0.4, 0.2, 0.6, 1.2, -0.5, 0.5, 0.0, 10.0};
  sortkeys(C, 9);
  std::cout << std::endl;
  return 0;
}