并⾏排序算法

描述:

使用基于oneAPI的C++/SYCL实现⼀个高效的并行归并排序。需要考虑数据的分割和合并以及线程之间的协作。

分析&示例:

归并排序是⼀种分治算法,其基本原理是将待排序的数组分成两部分,分别对这两部分进行排序,然后将已排序的子数组合并为⼀个有序数组。可考虑利用了异构并行计算的特点,将排序和合并操作分配给多个线程同时执行,以提高排序效率。具体实现过程如下:

  1. 将待排序的数组分割成多个较小的子数组,并将这些⼦数组分配给不同的线程块进行处理。
  2. 每个线程块内部的线程协作完成子数组的局部排序。
  3. 通过多次迭代,不断合并相邻的有序⼦数组,直到整个数组有序。
    在实际实现中,归并排序可使用共享内存来加速排序过程。具体来说,可以利用共享内存来存储临时数据,减少对全局内存的访问次数,从而提高排序的效率。另外,在合并操作中,需要考虑同步机制来保证多个线程之间的数据⼀致性。需要注意的是,在实际应用中,要考虑到数组大小、线程块大小、数据访问模式等因素,来设计合适的算法和参数设置,以充分利用目标计算硬件GPU的并行计算能力,提高排序的效率和性能。

代码

#include <math.h>
#include <iostream>
#include <string>
#include <optional>
#include <fstream>
#include "dpc_common.hpp"

using namespace sycl;
using namespace std;

#define DEBUG 0

// 初始化数组
void initializeArr(double* arr, string line) {
    int index = 0;
    istringstream iss(line);
    double value;
    while (iss >> value) {
        arr[index++] = value;
    }
}

// 获取数组大小
int getSize(string line) {
    int res = 0;
    istringstream iss(line);
    double value;
    while (iss >> value) {
        res ++;
    }
    return res;
}

// 获取n的值
int getN(int size) {
    int res = 1;
    int x = 2;
    while(x<size) {
        res += 1;
        x *= 2;
    }
    return res;
}

// 获取新的数组大小
int getNewSize(int n) {
    int res = 1;
    while(n--) {
        res *= 2;
    }
    return res;
}

// 并行比特位排序
void ParallelBitonicSortBuffer(double data_arr[], int n, queue &q) {
    // n: 用于设置数组大小的指数。数组大小 = 2的n次方

    int size = pow(2, n);

    buffer input(data_arr, range(size));

    // 从0, 1, 2, ..., n-1进行迭代
    for (int step = 0; step < n; step++) {
        // 对于每个步骤s,阶段从s, s-1, ..., 0进行迭代
        for (int stage = step; stage >= 0; stage--) {
            int seq_len = pow(2, stage + 1);

            // 在内核中使用的常数: 2的(step-stage)次方
            int two_power = 1 << (step - stage);

            // 将工作卸载到内核中
            q.submit([&](auto &h) {
                accessor a(input, h);

                h.parallel_for(size, [=](id<1> i) {
                    // 分配比特位序列号
                    int seq_num = i / seq_len;

                    // 用于标识交换元素的变量
                    int swapped_ele = -1;

                    // 因为比特位序列中的前半部分的元素可能与后半部分的元素交换,
                    // 所以只需要比特位序列中的前半部分元素(seq_len/2)。
                    int h_len = seq_len / 2;

                    if (i < (seq_len * seq_num) + h_len)
                        swapped_ele = i + h_len;

                    // 检查是“递增”还是“递减”顺序
                    int odd = seq_num / two_power;

                    // 用于确定“递增”还是“递减”顺序的布尔变量
                    bool increasing = ((odd % 2) == 0);

                    // 如果需要,交换比特位序列中的元素
                    if (swapped_ele != -1) {
                        if (((a[i] > a[swapped_ele]) && increasing) ||
                            ((a[i] < a[swapped_ele]) && !increasing)) {
                            double temp = a[i];
                            a[i] = a[swapped_ele];
                            a[swapped_ele] = temp;
                        }
                    }
                });
            });
        } // 结束阶段
    } // 结束步骤
}

// 显示数组的函数
void DisplayArray(double a[], int array_size) {
    for (int i = 0; i < array_size; ++i) cout << a[i] << " ";
    cout << "\n";
}

int main(int argc, char *argv[]) {
    int seed = 9, size;

    string filename = "problem-2.txt";
    ifstream infile(filename);
    string line;
    getline(infile, line);

    size = getSize(line);
    int n = getN(size);
    int newSize = getNewSize(n);

    cout << "\n数组大小: " << size << "\n";

    // 在实现选择的默认设备上创建队列
    queue q;

    cout << "设备: " << q.get_device().get_info<info::device::name>() << "\n";

    //分配仅用于主机访问的内存
    double *data_arr = (double *)malloc(newSize * sizeof(double));
    initializeArr(data_arr, line);
    for(int i=size;i<newSize;i++) {
        data_arr[i] = 100000000;
    }

    // 开始计时
    dpc_common::TimeInterval t_par1;

    // 使用缓冲区分配的并行排序
    ParallelBitonicSortBuffer(data_arr, n, q);

    cout << "使用缓冲区分配的内核时间: " << t_par1.Elapsed()
        << " 秒\n";

    std::ofstream outputFile("problem-2-result.txt");
    for (int i = 0; i < size - 1; i++) {
        outputFile << std::fixed << data_arr[i];
        outputFile << " ";
    }
    outputFile.close();

    return 0;
}

这段代码实现了并行比特位排序算法。
最开始,引用头文件和命名空间,定义宏和函数。

其中,定义了一个宏DEBUG,并且实现了一些辅助函数。initializeArr函数用于将字符串解析为数组并初始化。getSize函数用于获取字符串中的元素个数。getN函数用于根据数组大小获取迭代次数。getNewSize函数用于根据迭代次数获取新的数组大小。

并行比特位排序函数

void ParallelBitonicSortBuffer(double data_arr[], int n, queue &q) 

这是并行比特位排序的核心函数。它接受一个数组、迭代次数和SYCL队列作为参数。在函数内部,它使用SYCL的缓冲区和访问器来对数组进行并行排序。

void DisplayArray(double a[], int array_size)

这个函数用于在控制台上显示数组的内容。

main是程序的入口点。在主函数中,它从文件中读取数组数据,并根据数组大小计算迭代次数和新的数组大小。然后,它创建了一个SYCL队列,并调用并行比特位排序函数来对数组进行排序。排序完成后,结果将写入一个输出文件中。

整体来说,这段代码实现了一个并行比特位排序算法,利用SYCL的并行计算能力来提高排序效率。通过将工作分配到SYCL内核中进行并行处理,可以加快排序速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值