我有n个集合,它们是一个有限宇宙的子集。我想计算n×n矩阵,其中(I,J)条目包含集合I和集合J的交集的基数。 n的数量级为50000。
我的想法是将矩阵分成足够小的块,以便每个条目都有一个线程。每个线程应使用按位与运算符计算交集。
是否有更有效的方法来解决这个问题?
我的想法是将矩阵分成足够小的块,以便每个条目都有一个线程。每个线程应使用按位与运算符计算交集。
是否有更有效的方法来解决这个问题?
A[i,j] = sum((k in v[i]) * (k in w[j]) for k in the_universe)
其中v
和w
是两个集合列表,k在S中
如果为真则为1
,否则为0
。关键是要对索引进行排列,以使k
在外部循环而不是内部循环中,尽管出于效率考虑,您必须一次处理许多连续的k
,而不是一次一个。
也就是说,您将输出矩阵初始化为全零,并且对于每个1024个元素的宇宙块,您将计算交集的大小,并将结果累加到输出矩阵中。
我选择了1024,因为我想象你会有一个数据布局,在那里这可能是最小的大小,当从设备内存读取时,仍然可以获得完整的内存带宽,并且warp中的所有线程都可以一起工作。(如果您比我更了解,或者您不使用nVidia和您正在使用其他GPU,则应适当调整此参数)
现在,您的元素具有合理的大小,您现在可以使用传统的线性代数优化来计算此乘积。我可能会执行以下操作:
每个warp被分配了输出矩阵的大量行。它从第二个向量中读取相应的元素,然后迭代第一个向量,计算乘积。这种优化应该能将内存读取总数减少2倍,因此有望将性能提高一倍。
更好的方法是让每个块同时拥有3-4行输出矩阵,并将第二个向量的相应3-4个元素加载到寄存器中。然后块遍历第一个寄存器的所有元素,并为其计算它可以处理的3-4个交集,将结果存储在输出矩阵中。
这种优化将额外地将内存流量降低3-4倍。
一种完全不同的方法是分别处理宇宙中的每个元素:对于宇宙中的每个元素,计算实际包含该元素的集合,然后(原子性地)增加输出矩阵的相应条目。
从渐近意义上讲,这应该比计算集合的交集更有效。不幸的是,这听起来很难高效实现。
k
组,每组包含n/k
个集合。然后,第(i,j)
个线程(或warp或block)仅更新输出矩阵相应块的值。保留html标签。
n*n/2
个元素。 - Slizzered