“top-K数组元素”的并行半在线实现?

时间:2016-08-12 21:01:13

标签: multithreading algorithm time-complexity

设A是位于主存储器中的n个整数的数组,让k<< n并假设我有一个带有m个内核的单插槽CPU,每个内核都有自己的L1缓存,并且都有一个共同的L2缓存。

我想从n中获取k个最高值的元素(比如说,进入预分配的输出数组B);而且 - 我希望这在实际上是快速的(所以渐近​​O(*)都必须是好的并且常数必须很低)。而且我也有一个约束:核心之间没有同步,也没有复杂的原子指令(例如比较和交换测试和设置);整数读写都是原子的。

两种直接的方法是:

  1. 每个核心维护自己最多k个元素的最小堆,用它从相关的1 / m内存中读取的数据更新它(这种关联可以交错以获得更好的性能)。
  2. 所有核心都以某种方式并行维护一个堆(无锁堆?)
  3. 如果每个核心的堆都适合其L1,则方法1更有前景;否则它可能不值得麻烦(取决于我想的内存带宽和插入频率)。

    另外,我考虑从某种抽样开始,对保守猜测一个截止值,我们不打算插入堆中,以避免插入过多。

    您会考虑采用完全不同的方法吗?或者通过某种额外的技巧进一步改善我的想法?

    备注:

    • 输入是只读的,即输入数据的任何重新排序只能在不合适的地方发生。
    • 请忽略其他细节,例如究竟是什么样的微架构,是否存在“超线程”等。如果您认为这是必不可少的,请在评论中这样说。
    • 最好(但不是必须)额外的存储器复杂度仅取决于k和m而不是n。

1 个答案:

答案 0 :(得分:0)

您可以将答案基于https://en.wikipedia.org/wiki/Quickselect而不是堆。由于quickselect是线性时间,这将为您节省渐近复杂度的log n因子。您还可以选择使用并行处理来运行quickselect的每个阶段,而不是将数据分成m个部分并运行m个独立的quickselects。 (从这里编辑)

将数据拆分成m个部分并分别处理每个部分相对容易,这样做是适用于其他问题的,所以为了保持一致性,这样做可能是值得的,所以让我们继续。

在O(mk)可写空间(实际上每个处理器为2k)而不是O(n)中执行此操作。只考虑一个处理器。将数据划分为大小为k的块。取前两个块并运行quickselect以找到那些2k中的前k个。附加下一个k项的块以获得大小为2k的块并重复以获得前3k数据项的前k项。继续使用大块的k项,直到达到该处理器的工作量结束。有n / m项需要考虑,我们总共只有不到n /(mk)的quickselect运行要做,每个运行需要时间O(m)所以这个成本是O(n / m) - 这个处理器需要处理的数据是线性的。