所以,我正在尝试在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;
}
}
}
我在这里的主要尝试是找到最小化并并行化解决方案。现在,我意识到代码看起来非常C ++'但是我没有资格熟练的Cuda。
有没有办法并行化解决方案?还有更多的补充吗?
答案 0 :(得分:0)
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
第一部分(找到最大元素)属于众所周知且记录良好的问题类别,称为 reduction 。但是,要执行第二部分(交换),您必须在比较值时跟踪最大元素的索引,并且在执行缩减时执行此操作并不是那么自然。这是选择排序不能很好地适应并行体系结构的原因之一。
此外,您可以看到每个循环的问题大小减少一个,这是选择排序算法的另一个方面,它不能很好地映射到并行体系结构。在CUDA的情况下,32个线程形成 warp ,它们同时执行。虽然你可以告诉任意数量的线程在warp中运行,但通常不建议这样做,因为它会损失计算能力。
我自己试图建立一个CUDA版本的选择排序,但我停止了这样做,因为它似乎有更好的算法非常适合CUDA。但是我会告诉你到目前为止我做了什么来说明为什么选择排序对CUDA不利。
首先,从一个小而简单的问题开始:排序32个元素。由于32个线程形成扭曲,因此您可以使用shuffle instructions来查找最大值。 (Full code)
// 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 ...
}
(有些人可能认为这不是一个选择排序,因为1)未排序区域的数组元素保持混乱,2)它不是就地排序。请注意,我只是想表明选择排序不适合CUDA。另请注意,warpMax
具有高度不同的分支,使其不太适合CUDA。)
只有1个元素扭曲的情况可能看起来是平行的,但是当问题大小增加到多个warp时情况变得更糟。让我们看看1024个元素的情况。 (我选择了数字1024,因为它是一个块中线程的最大数量限制。)现在有32个warp,并且在为每个warp调用warpMax
之后,我们必须将每个warp的最大元素进行比较获取1024个元素中的最大元素。使用warpMax
无法比较32个warp-maximum-values的问题,因为我们需要跟踪最大值来自哪个warp以将最大值与数据数组中的最后一个元素交换。我可以想到的一种方法是使用一个单独的线程来比较warp-maximum-values。这对CUDA来说不是一个好的实现,因为块中的其他1023个线程变得空闲。
此外,如果问题大小比块可以覆盖的大,我们需要比较每个块的最大值,这意味着我们必须启动单独的内核,因为我们需要在块之间进行同步。并且说我们需要跟踪最大值来自哪个块是多余的。所有这些只是说明为CUDA实现选择排序不是一个好主意。