对于我的应用程序,我必须处理一堆对象(比如int
s),这些对象随后被分割并分类为更小的桶。为此,我将元素存储在一个连续的数组中
arr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14...}
有关存储桶(子列表)的信息由相应存储桶中第一个元素的偏移量和子列表的长度给出。
所以,例如,给定
offsets = {0,3,8,..}
sublist_lengths = {3,5,2,...}
会导致以下分割:
0 1 2 || 3 4 5 6 7 || 8 9 || ...
我正在寻找的只是使用自定义内核或thrust
库在桶上运行算法(如减少)的一种通用且有效的方法。总结水桶应该给出:
3 || 25 || 17 || ...
我想出了什么:
选项1 :自定义内核需要相当多的修改,复制到共享内存,正确选择块和网格大小以及自己的算法实现,如扫描,减少此外,每个操作都需要一个自己的自定义内核。一般来说,我很清楚如何做到这一点,但在过去几天使用thrust
后,我觉得可能有更聪明的方式
选项2 :从偏移量生成一组键(上例中为{0,0,0,1,1,1,1,1,2,2,3,...}
)并使用thrust::reduce_by_key
。不过,我不喜欢额外的列表生成。
选项3 :将thrust::transform_iterator
与thrust::counting_iterator
一起使用,即时生成上面给出的密钥列表。不幸的是,我无法想出一个不需要将索引增加到设备上的偏移列表并实现并行性的实现。
实现这一目标的最理智的方法是什么?
答案 0 :(得分:4)
在Thrust中,我想不出比选项2更好的解决方案。性能不会很糟糕,但肯定不是最佳的。
您的数据结构与用于存储稀疏矩阵的Compressed Sparse Row (CSR)格式具有相似性,因此如果您想要更好的性能,可以使用为此类矩阵计算sparse matrix-vector multiplies (SpMV)的技术。请注意,CSR格式的“偏移”数组对于具有N行的矩阵(即您的情况下的桶)具有长度(N + 1),其中最后一个偏移值是arr
的长度。 CSR SpMV code中的Cusp有点复杂,但它可以作为内核的良好起点。只需从代码中删除对Aj
或x
的任何引用,并将offsets
和arr
分别传递到Ap
和Av
参数中。
答案 1 :(得分:1)
你没有提到水桶有多大。如果存储桶足够大,也许您可以将偏移和sublist_length复制到主机,迭代它们并为每个存储桶执行单独的Thrust调用。 Fermi可以同时运行16个内核,因此在该架构上,您可以处理较小的存储桶并且仍然可以获得良好的利用率。