【问题标题】:Is a binary search a good fit for OpenCL?二进制搜索是否适合 OpenCL?
【发布时间】:2014-09-19 07:10:28
【问题描述】:

我正在编写一个 OpenCL 程序 - 除其他外 - 需要根据白名单检查计算的 (int) 值。我的计划是将白名单存储在常量或共享内存中,然后让每个线程使用此共享白名单运行二进制搜索。

然后我读到了诸如银行冲突之类的事情,其中​​线程变慢了,因为它们正在访问同一银行上的内存,这会导致访问发生序列化。

由于此类问题,二分搜索是否会在 OpenCL 上导致更大的性能损失?使用其他搜索算法(例如哈希)会更好吗?

编辑 让我澄清一下我的程序:

每个线程都会进行并行计算,但输入值不同。因此,每个线程都会得到不同的输出。每个输出都需要根据同一个白名单进行检查。

内核会返回一个布尔值,表示搜索的结果。

我担心的是,由于每个线程都在进行独立的二进制搜索,因此多个线程最终会访问白名单的同一个银行,从而导致串行速度变慢。

【问题讨论】:

  • OpenCL 位操作的一个很好的例子是根本不进行计算(所有内存操作)的情况。这样减少全局内存的带宽需求会很有帮助。 -> stackoverflow.com/questions/20039890/…
  • 我之前在 OpenCL 中做过二进制搜索;它们比 CPU 实现速度更快,但速度并不快。条件代码中存在一些分歧,但不会造成太大影响。

标签: c++ algorithm search opencl binary-search


【解决方案1】:

如果您在每个线程中搜索不同的项目,那么与其担心银行冲突,您需要担心线程分歧,因为二进制搜索需要分支。使用select 函数可以缓解其中一些问题。

使用其他算法可能会更好,例如interpolation search,它可以在更少的跳跃中找到项目(本质上,下一步查找的决定比二分搜索更昂贵,但如果您的搜索数据在全局内存,您可以在内存延迟下隐藏大量处理(大约 20 条指令)。

我最近也在解决一个类似的问题:Binary search with hint。

一个简化的算法如下所示:

__global const _TyIndex *upper_bound(__global const _TyIndex *begin,
    __global const _TyIndex *end, const _TyIndex elem)
{
    while(begin != end) {
        __global const _TyIndex *mid = begin + (end - begin) / 2;

#if 0
        if(!(elem < *mid))
            begin = mid + 1; // look to the right
        else
            end = mid; // look to the left
#else // 0
        bool b_right = !(elem < *mid);
        begin = (__global const _TyIndex *)select((intptr_t)begin, (intptr_t)(mid + 1), b_right);
        end = (__global const _TyIndex *)select((intptr_t)mid, (intptr_t)end, b_right); // c ? b : a
#endif // 0
    }

    return begin;
}

这是使用select() 两次而不是分支。您可以通过将#if 0 更改为#if 1 来比较性能。请注意,e? a : b 确实暗示了一个分支,因此使用它并没有帮助。

【讨论】:

  • 很好,这就是我想要的。只是为了确保我理解:interpol 比 GPU 上的二进制更好,因为更复杂的预测的成本低于访问全局内存位置的成本。
  • 是的,GPU 的工作方式也称为“深 pieplines”,这意味着执行指令不是一次完成(如在 RISC 架构中通常那样),而是有多个执行阶段,因此一条指令需要很长时间(许多时钟周期)才能执行。但这并不会让它变慢,因为这条流水线的每个阶段都可以处理不同的指令(因此吞吐量相同,延迟更长)。
  • 在类比中,访问内存需要很多时间,但这不会使 GPU 空闲,因为还有其他功能单元仍然可以执行代码,有潜在的休眠线程可以被唤醒等(尽管其中一部分也是乱序执行,而不仅仅是深度流水线)。实际上,每个内存访问都有可能隐藏一些非内存访问工作(计算),所以不使用它会很可惜。
  • 注意为使用select的优化二分搜索提供一些伪代码?
  • @Jonno_FTW 你去吧
【解决方案2】:

听起来您正在每个内核中进行二进制搜索作为其他工作的一部分。最好先找到搜索的结果,然后将结果作为参数传递给内核。

一般来说,二分搜索是一种 logn 算法,除非您在非常大的列表中查找值,否则应该不会很慢。如果您在每个内核中进行相同的搜索,仍然会浪费资源。而且,如果您想并行化搜索本身,它仍然效率低下,因为在算法的每个级别/迭代中您只有 2 个内核执行内核。将任何其他主程序添加到此 -> opencl 开销。最好进行线性搜索,将白名单拆分为与内核数量一样多的部分。在列表的单个部分中查找值可能需要比二分查找更长的时间,但在主程序和内核之间传递值的开销会更少。

【讨论】:

  • 我已经稍微澄清了我的问题,请看看这是否会改变你的答案。
  • @stan0 好吧,正式地,二分搜索是O(log N),而您的线性搜索是O(N / T),其中T 是线程数。因此,只有T &gt; N / log(N) 才能真正更快,而且这还不包括为获得最终是/否(或索引)而减少的结果。 Merill / Garland 有一篇关于 GPU 上的并行树遍历的论文,不确定,但我认为可以执行例如并行interval halving.
【解决方案3】:

如果预先计算的整数列表没有改变(只读数据)并且线程没有修改它,那么在没有同步的情况下使用二进制搜索进行搜索是完全可以的。由于同步问题,线程可能会变慢。散列有时比二分查找更快,但它对更大的数组更有用。先试试二分查找。

您还可以看到: Is it wise to access read-only data from multiple threads simultaneously?

【讨论】:

    猜你喜欢
    • 2011-02-19
    • 1970-01-01
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-05
    • 2014-07-10
    相关资源
    最近更新 更多