二进制搜索是否适合OpenCL

Is a binary search a good fit for OpenCL?

本文关键字:OpenCL 是否 搜索 二进制      更新时间:2023-10-16

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

然后我读到像银行冲突这样的事情,线程因为访问同一银行上的内存而变慢,这会导致访问的序列化发生。

由于这样的问题,二进制搜索会导致OpenCL上更大的性能损失吗?用其他的搜索算法会更好吗,比如散列?

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

每个线程将执行并行计算,但使用不同的输入值。因此,每个线程将获得不同的输出。每个输出都需要根据相同的白名单进行检查。

内核将返回一个bool值,该值表示搜索结果。

我担心的是,由于每个线程都在进行独立的二进制搜索,因此多个线程最终将访问白名单的同一组,从而导致连续减速。

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

你可以更好地使用其他算法,比如插值搜索,它可以在更少的跳数中找到项目(本质上,决定下一步在哪里查找比二进制搜索更昂贵,但如果你的搜索数据在全局内存中,你可以隐藏很多处理(大约20条指令)在内存延迟下)。

我最近正在解决一个类似的问题:带提示的二进制搜索。

一个简化的算法是这样的:

__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确实暗示了一个分支,所以使用它没有帮助。

如果预先计算的int类型列表没有改变(只读数据),并且线程没有修改它,那么使用不同步的二进制搜索是完全可以的。线程可能因为同步问题而变慢。哈希有时比二分查找更快,但它对更大的数组更有用。先用二进制搜索。

你也可以看到:同时从多个线程访问只读数据是明智的吗?

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

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