Sorting Cuda中的选择排序

Sorting Cuda中的选择排序,sorting,cuda,Sorting,Cuda,所以,我尝试在Cuda中实现选择排序,但到目前为止我还没有成功 __device__ void selection_sort( int *data, int left, int right ){ for( int i = left ; i <= right ; ++i ){ int min_val = data[i]; int min_idx = i; // Find the smallest value in the range [le

所以,我尝试在Cuda中实现选择排序,但到目前为止我还没有成功

__device__ void selection_sort( int *data, int left, int right ){

    for( int i = left ; i <= right ; ++i ){
        int min_val = data[i];
        int min_idx = i;

    // Find the smallest value in the range [left, right].
        for( int j = i+1 ; j <= right ; ++j ){
            int val_j = data[j];
            if( val_j < min_val ){
                min_idx = j;
                min_val = val_j;
            }
        }

        // Swap the values.
        if( i != min_idx ){
            data[min_idx] = data[i];
            data[i] = min_val;
        }
    }
}
\uuuuu设备\uuuuu无效选择\u排序(int*数据,int左,int右){

对于(int i=left;i而言,
N
数字的选择排序算法可以大致描述为:

for i from N-1 down to 0
    find the maximum element among data[0] ~ data[i]
    swap that maximum element with data[i] within the data array
第一部分(寻找最大元素)属于一类广为人知且有充分记录的问题,称为缩减。然而,执行第二部分(交换),您必须在比较值时跟踪最大元素的索引,而在执行缩减时这样做并不自然。这就是选择排序不能很好地移植到并行体系结构的原因之一

此外,您可以看到,每个循环的问题大小会减少一个,这是选择排序算法的另一个方面,它不能很好地映射到并行架构。在CUDA的情况下,32个线程形成一个扭曲,同时执行。尽管您可以告诉任意数量的线程在一个扭曲中运行,但通常情况下不是这样建议这样做,因为这是一种计算能力的损失

我自己也尝试过构建CUDA版本的选择排序,但我停止了,因为似乎有更好的算法非常适合CUDA。但我将向您展示我迄今为止所做的工作,以说明为什么选择排序不适合CUDA

首先,从一个小而简单的问题开始:排序32个元素。由于32个线程形成一个扭曲,您可以使用来查找最大值。()

//查找扭曲中的最大元素,并将最大元素提供给
//通道id为0的线程。请注意,其他元素不会丢失,但它们的
//位置被洗牌。
__内联\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu设备\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
{
对于(int mask=16;mask>0;mask/=2){
int dual_data=_shfl_xor(数据,掩码,32);
if(线程ID和掩码)
数据=最小值(数据,双_数据);
其他的
数据=最大值(数据,双_数据);
}
返回数据;
}
__全局无效选择32(int*d\u数据,int*d\u数据已排序)
{
无符号int-threadId=blockIdx.x*blockDim.x+threadIdx.x;
unsigned int-laneId=threadIdx.x%32;
int n=n;
而(n-->0){
//获取d_数据中的最大元素,并将其放入d_数据中[n]
int data=d_data[threadId];
数据=warpMax(数据,线程ID);
d_data[threadId]=数据;
//现在,最大元素位于d_数据[0]中
if(laneId==0){
d_数据_排序[n]=d_数据[0];
d_data[0]=INT_MIN;//从此将忽略此元素
}
}
}
int main()
{
//…生成数据并传输到d_数据。。。
选择32(d_数据,d_数据已排序);
//…获取存储在d_data_sorted处的已排序数组。。。
}
(有些人可能会认为这并不完全是一种选择排序,因为1)未排序区域的数组元素一直在混洗,2)它不是就地排序。请注意,我只是想说明选择排序不适合CUDA。另外,请注意,
warpMax
具有高度不同的分支,这使得它对CUDA的优化程度较低。)

只有1个扭曲元素的情况可能看起来是平行的,但当问题大小增加到多个扭曲时,情况会变得更糟。让我们看看1024个元素的情况。(我选择了1024个,因为它是块中线程的最大数量限制。)现在有32个扭曲,在为每个扭曲调用
warpMax
后,我们必须比较每个扭曲的最大元素,以获得1024个元素中的最大元素。比较32个扭曲最大值的问题无法通过
warpMax
完成,因为我们需要跟踪最大值来自哪个扭曲来交换扭曲数据数组中最后一个元素的最大值。我可以想到的一种方法是使用一个线程来比较扭曲最大值。这对于CUDA来说不是一个好的实现,因为块中的其他1023个线程变为空闲


此外,如果问题的大小超过了一个块所能覆盖的范围,我们需要比较每个块的最大值,这意味着我们必须启动单独的内核,因为我们需要在块之间进行同步。说我们需要跟踪最大值来自哪个块是多余的。所有这些都只是告诉我们为CUDA实现选择排序不是一个好主意。

IMHO,我不认为选择排序可以并行重写。如果您需要并行排序解决方案,请尝试bubble/merge/bitonic排序。我不理解您的问题。您发布的是一个设备函数。设备函数是由单个线程运行的函数,并且是从内核内部调用。根据它们的定义,它们是串行操作。那么,当你说你“没有成功”时,axcatly是什么意思?当你说你想“并行化解决方案”时,在这个
\uuu device\uuuuu
函数的上下文中,这到底意味着什么?经典的比较排序算法不能很好地映射到多处理器体系结构。并行排序是一个仍在研究中的问题,也是一个相当困难的问题。您可能需要先从更简单的东西开始。但是,如果您有兴趣并想了解不管怎样,检查CUDA工具包附带的快速排序样本和推力库中的排序功能。
// Finds the maximum element within a warp and gives the maximum element to
// thread with lane id 0. Note that other elements do not get lost but their
// positions are shuffled.
__inline__ __device__ int warpMax(int data, unsigned int threadId)
{
    for (int mask = 16; mask > 0; mask /= 2) {
        int dual_data = __shfl_xor(data, mask, 32);
        if (threadId & mask)
            data = min(data, dual_data);
        else
            data = max(data, dual_data);
    }
    return data;
}

__global__ void selection32(int* d_data, int* d_data_sorted)
{
    unsigned int threadId = blockIdx.x * blockDim.x + threadIdx.x;
    unsigned int laneId = threadIdx.x % 32;

    int n = N;
    while(n-- > 0) { 
        // get the maximum element among d_data and put it in d_data_sorted[n]
        int data = d_data[threadId];
        data = warpMax(data, threadId);
        d_data[threadId] = data;

        // now maximum element is in d_data[0]
        if (laneId == 0) {
            d_data_sorted[n] = d_data[0];
            d_data[0] = INT_MIN; // this element is ignored from now on
        }
    }
}

int main()
{
    // ... build data and trasfer to d_data ...
    selection32<<<1, 32>>>(d_data, d_data_sorted);
    // ... get the sorted array stored at d_data_sorted ...
}