Algorithm CUDA流压缩算法
我正在尝试用CUDA构造一个并行算法,它接受一个整数数组,并删除所有Algorithm CUDA流压缩算法,algorithm,parallel-processing,cuda,stream-compaction,Algorithm,Parallel Processing,Cuda,Stream Compaction,我正在尝试用CUDA构造一个并行算法,它接受一个整数数组,并删除所有0,无论是否保持顺序 例如: 全局内存:{0,0,0,0,14,0,0,0,17,0,0,0,0,13} 主机内存结果:{17,13,14,0,0,…} 最简单的方法是使用主机删除O(n)时间中的0。但是考虑到我有大约1000个元素,在发送之前,将所有元素都放在GPU上并进行压缩可能会更快 首选的方法是创建一个设备上堆栈,这样每个线程都可以弹出并推(以任何顺序)到堆栈上或从堆栈上推出。然而,我认为CUDA没有实现这一点 一种等效
0
,无论是否保持顺序
例如:
全局内存:{0,0,0,0,14,0,0,0,17,0,0,0,0,13}
主机内存结果:{17,13,14,0,0,…}
最简单的方法是使用主机删除O(n)
时间中的0
。但是考虑到我有大约1000个元素,在发送之前,将所有元素都放在GPU上并进行压缩可能会更快
首选的方法是创建一个设备上堆栈,这样每个线程都可以弹出并推(以任何顺序)到堆栈上或从堆栈上推出。然而,我认为CUDA没有实现这一点
一种等效(但要慢得多)方法是继续尝试写入,直到所有线程都完成写入:
kernalRemoveSpacing(int * array, int * outArray, int arraySize) {
if (array[threadId.x] == 0)
return;
for (int i = 0; i < arraySize; i++) {
array = arr[threadId.x];
__threadfence();
// If we were the lucky thread we won!
// kill the thread and continue re-reincarnated in a different thread
if (array[i] == arr[threadId.x])
return;
}
}
kernalremovespace(int*array,int*outArray,int-arraySize){
if(数组[threadId.x]==0)
返回;
for(int i=0;i
这种方法的唯一好处是,我们将在O(f(x))
时间中执行,其中f(x)
是数组中存在的非零值的平均数(f(x)~=ln(n)
对于我的实现,因此O(ln(n))
时间,但具有较高的O
常数)
最后,像quicksort或mergesort这样的排序算法也可以解决这个问题,而且实际上是在O(ln(n))
相对时间内运行的。我认为可能有一种算法比这更快,因为我们不需要浪费时间排序(交换)零零元素对和非零非零元素对(不需要保持顺序)
所以我不确定哪种方法最快,我仍然
我想有更好的方法来处理这件事。有什么建议吗
您需要的是一个名为streamcompaction1的经典并行算法 如果推力是一个选项,您可以简单地使用。这是一个稳定的算法,它保留了所有元素的相对顺序 草图:
#include <thrust/copy.h>
template<typename T>
struct is_non_zero {
__host__ __device__
auto operator()(T x) const -> bool {
return x != 0;
}
};
// ... your input and output vectors here
thrust::copy_if(input.begin(), input.end(), output.begin(), is_non_zero<int>());
#包括
模板
结构为非零{
__主机设备__
自动运算符()(T x)常量->布尔{
返回x!=0;
}
};
// ... 在这里输入和输出向量
推力::复制如果(input.begin()、input.end()、output.begin()、是非零();
如果推力不是一个选项,您可以自己实现流压缩(关于这个主题有很多文献)。这是一个有趣且相当简单的练习,同时也是更复杂的并行原语的基本构建块
(1)严格来说,它不是传统意义上的流压缩,因为流压缩传统上是一种稳定的算法,但您的要求不包括稳定性。这种宽松的要求可能会导致更高效的实现?流压缩是一个众所周知的问题,为此编写了很多代码(Struch,Chagg引用了两个在CUDA上实现流压缩的库) 如果您有一个相对较新的支持CUDA的设备,该设备支持“投票”(计算能力>=3.0)的固有功能,那么值得尝试一个小型CUDA程序,该程序执行流压缩比推力快得多 这里查找代码和最小文档。 Is以单个内核的方式使用抽签函数来执行压缩
编辑:
我写了一篇文章解释这种方法的内部工作原理。如果您感兴趣,可以找到它。有了这个答案,我只想提供更多关于戴维德·斯帕塔罗方法的细节 正如您所提到的,流压缩包括根据谓词删除集合中不需要的元素。例如,考虑整数数组和谓词
p(x)=x>5
,数组A={6,3,2,11,4,5,3,7,5,77,94,0}
被压缩为B={6,11,7,77,94}
流压缩方法的一般思想是将不同的计算线程分配给要压缩的数组的不同元素。每个这样的线程都必须决定将其对应的元素写入输出数组,这取决于它是否满足相关谓词。因此,流压缩的主要问题是让每个线程知道必须在输出数组中写入相应元素的位置
[1,2]中的方法是上述推力的copy\u的替代方法,包括三个步骤:
第1步。设P
为已启动线程的数量,N
,其中N>P
,为要压缩的向量的大小。将输入向量划分为大小S
等于块大小的子向量。利用\uuuu syncthreads\u count(pred)
块内在特性,计算满足谓词pred的块中的线程数。作为第一步的结果,数组d_BlockCounts
的每个元素(其大小N/P
)包含在相应块中满足谓词pred的元素数
第二步。对阵列d_块计数执行独占扫描操作。作为第二步的结果,每个线程都知道前面块中有多少个元素写入一个元素。因此,它知道写入其相应元素的位置,但与自身块相关的偏移量除外
第三步。每个线程使用warp内在函数计算上述偏移量,并最终写入输出数组。应该注意,步骤3的执行与t相关
[1] M.Biller, O. Olsson, U. Assarsson, “Efficient stream compaction on wide SIMD many-core architectures,” Proc. of the Conf. on High Performance Graphics, New Orleans, LA, Aug. 01 - 03, 2009, pp. 159-166.
[2] D.M. Hughes, I.S. Lim, M.W. Jones, A. Knoll, B. Spencer, “InK-Compact: in-kernel stream compaction and its application to multi-kernel data visualization on General-Purpose GPUs,” Computer Graphics Forum, vol. 32, n. 6, pp. 178-188, 2013.