OpenCL实现的归并排序

本文给出一个归并排序算法的例子。本例子支持任意数量元素的数组。代码是从DeepSeek上得到的,只是把C语言的OpenCL库换成了C++版的。归并排序是通过两个有序子数组之间不断地合并实现的。从1个元素的两个子数组开始合并,然后是2个元素的两个子数组合并,然后是4个元素的两个子数组合并……直到最后把整个数组合并成有序数组。下述代码也是这样实现的,代码中segmentSize就表示子数组的大小,对应的在核函数中索引从segment_start到segment_mid是第一个子数组,从segment_mid到segment_end是第二个子数组,把这两个子数组合并。本算法运行环境是VS2019、OpenCL3,CPU是Intel i5,显卡是英伟达的4060。

本例不需要头文件,下面是CPP文件:

const string strKernel = R"(
__kernel void merge_sort(
    __global const float* input,   // 输入数组
    __global float* output,        // 输出数组
    const int segment_size,        // 当前每个有序段的大小
    const int total_size           // 数组总大小
) {
    // 获取全局线程ID
    int gid = get_global_id(0);
    // 计算当前线程需要合并的两个段的起始位置
    // 每个线程处理两个相邻的段
    int segment_start = gid * segment_size * 2;
    int segment_mid = min(segment_start + segment_size, total_size);
    int segment_end = min(segment_start + segment_size * 2, total_size);
    // 如果起始位置超出数组范围,直接返回
    if (segment_start >= total_size) {
        return;
    }
    // 初始化指针
    int i = segment_start;      // 左段指针
    int j = segment_mid;        // 右段指针
    int k = segment_start;      // 输出位置指针
    // 合并两个有序段
    while (i < segment_mid && j < segment_end) {
        if (input[i] <= input[j]) {
            output[k] = input[i];
            i++;
        } else {
            output[k] = input[j];
            j++;
        }
        k++;
    }
    // 复制左段剩余元素
    while (i < segment_mid) {
        output[k] = input[i];
        i++;
        k++;
    }
    // 复制右段剩余元素
    while (j < segment_end) {
        output[k] = input[j];
        j++;
        k++;
    }
})";

int main()
{
    cl::Program program(strKernel);
    try
    {
        program.build("-cl-std=CL2.0");
    }
    catch (...)
    {
        cl_int buildErr = CL_SUCCESS;
        auto buildInfo = program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(&buildErr);
        for (auto& pair : buildInfo)
        {
            std::cerr << pair.second << std::endl << std::endl;
        }
        return 0;
    }
    auto kernel = cl::KernelFunctor<cl::Buffer, cl::Buffer, int, int>(program, "merge_sort");

    /* 准备初始数据 */
    const int DATA_SIZE = 518;
    vector<float> number(DATA_SIZE, 100);
    theRNG().fill(number, RNG::UNIFORM, 0, 100); /* OpenCV的函数随机数初始化 */
    vector<float> summary(DATA_SIZE);

    cl::Buffer input(number.begin(), number.end(), false);
    cl::Buffer output(summary.begin(), summary.end(), false);
    int segmentSize = 1;
    bool useInputAsSource = true;
    cl::Buffer destBuffer;

    while (segmentSize < DATA_SIZE)
    {
        cl::Buffer sourceBuffer = useInputAsSource ? input : output; /* 核函数输入 */
        destBuffer = useInputAsSource ? output : input; /* 核函数输出 */

        size_t globalSize = (DATA_SIZE + (2 * segmentSize) - 1) / (2 * segmentSize); /* 全局线程数量 */
        kernel(cl::EnqueueArgs(cl::NDRange(globalSize), cl::NDRange()), sourceBuffer, destBuffer, segmentSize, DATA_SIZE);
        useInputAsSource = !useInputAsSource;
        segmentSize *= 2; /* 归并的一组元素数 1,2,4,8,16,32... */
    }
    cl::copy(destBuffer, summary.begin(), summary.end());
    for (auto item : summary)
    {
        cout << item << " ";
    }

    return 0;
}

算法运行结果如下图。

微信截图_20260115135349

 

posted @ 2026-01-15 13:54  兜尼完  阅读(1)  评论(0)    收藏  举报