基于oneAPI的SYCL归并排序加速实现

基于oneAPI的SYCL归并排序加速实现

任务概述

使用基于oneAPI的C++/SYCL实现一个归并排序操作。输⼊为乱序数字,输出顺序数字。

技术方案

(1)oneAPI、SYCL介绍

oneAPI 是一个由英特尔主导的开放、全面的软件栈,旨在简化多核和异构计算的编程。oneAPI 提供了一个统一的编程模型,可以用于不同类型的处理器,包括 CPU、GPU、FPGA 和其他加速器。通过 oneAPI,开发人员可以更轻松地利用各种处理器的性能优势,同时减少针对特定处理器类型的编程复杂性。

SYCL(pronounced “sickle”)是一种基于 C++ 的编程模型,用于实现异构计算。SYCL 的目标是提供一种简单且高性能的编程模型,以实现 CPU、GPU 和其他加速器之间的无缝协同。SYCL 基于标准的 C++,并引入了许多用于表示并行性和内存关系的新特性。它使开发人员能够使用标准的 C++ 代码编写并行程序,而无需深入了解特定硬件体系结构。

总的来说,oneAPI 为异构计算提供了统一的编程模型和工具集,而 SYCL 则是其中的一部分,专注于提供基于 C++ 的高性能并行编程模型。这些工具和模型使开发人员能够更轻松地利用各种处理器的潜力,并加快异构计算应用程序的开发和部署速度。

(2)归并排序

归并排序是一种经典的分治算法,它通过递归地将数组分成两半、对每一半进行排序,然后合并两个有序的子数组来实现整体排序。

(3)基于oneAPI的SYCL归并排序

实现mergeSort函数时,可以使用了buffer类来存储数据,并使用queue类来提交任务到设备执行

代码实现

传统归并排序

%%writefile lab/merge_classic.cpp
//==============================================================
// Copyright © Intel Corporation
//
// SPDX-License-Identifier: MIT
// =============================================================
#include <iostream>
#include <vector>
#include <chrono>

void merge(std::vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;

    std::vector<int> L(n1), R(n2);

    for (int i = 0; i < n1; ++i) {
        L[i] = arr[left + i];
    }
    for (int j = 0; j < n2; ++j) {
        R[j] = arr[mid + 1 + j];
    }

    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }

    // 将剩余的元素拷贝回原数组
    while (i < n1) {
        arr[k++] = L[i++];
    }
    while (j < n2) {
        arr[k++] = R[j++];
    }
}

void mergeSort(std::vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

std::vector<int> generateRandomArray(int size) {
    std::vector<int> arr(size);
    for (int i = 0; i < size; ++i) {
        // 生成 -10000 到 10000 之间的随机数
        arr[i] = (rand() % 20001) - 10000;
    }
    return arr;
}

int main() {
    int array_size = 100000;
    std::vector<int> data = generateRandomArray(array_size);
    
    auto start = std::chrono::high_resolution_clock::now(); // 获取开始时间
    mergeSort(data, 0, data.size() - 1);
    
    auto end = std::chrono::high_resolution_clock::now(); // 获取结束时间
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "运行时间: " << duration.count() << " 毫秒" << std::endl;

    return 0;
}

基于oneAPI的SYCL的归并排序

%%writefile lab/merge.cpp

#include <CL/sycl.hpp>
#include <iostream>
#include <vector>
#include <chrono>

using namespace sycl;

std::vector<int> generateRandomArray(int size) {
    std::vector<int> arr(size);
    for (int i = 0; i < size; ++i) {
        // Generate random numbers between 37 and 2064
        arr[i] = (rand() % 2028) + 37;
    }
    return arr;
}
void merge(queue &q, buffer<int, 1> &arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;

    // Create temporary arrays
    std::vector<int> L(n1), R(n2);

    // Use get_host_access without template arguments
    auto arr_acc = arr.get_host_access();

    // Copy data to temp arrays L[] and R[]
    for (int i = 0; i < n1; i++)
        L[i] = arr_acc[left + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr_acc[mid + 1 + j];

    // Merge the temp arrays back into arr[left..right]
    int i = 0;
    int j = 0;
    int k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr_acc[k] = L[i];
            i++;
        } else {
            arr_acc[k] = R[j];
            j++;
        }
        k++;
    }

    // Copy the remaining elements of L[], if any
    while (i < n1) {
        arr_acc[k] = L[i];
        i++;
        k++;
    }

    // Copy the remaining elements of R[], if any
    while (j < n2) {
        arr_acc[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(queue &q, buffer<int, 1> &arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(q, arr, left, mid);
        mergeSort(q, arr, mid + 1, right);

        int n1 = mid - left + 1;
        int n2 = right - mid;
        buffer<int, 1> left_buf(&arr.get_pointer()[left], range<1>(n1));
        buffer<int, 1> right_buf(&arr.get_pointer()[mid + 1], range<1>(n2));
        buffer<int, 1> temp_buf(range<1>(n1 + n2));

        {
            auto L = left_buf.get_access<sycl::access::mode::read>();
            auto R = right_buf.get_access<sycl::access::mode::read>();
            auto temp = temp_buf.get_access<sycl::access::mode::write>();

            parallel_for(range<1>(n1), [=](id<1> idx) {
                temp[idx] = L[idx];
            });

            parallel_for(range<1>(n2), [=](id<1> idx) {
                temp[n1 + idx] = R[idx];
            });
        }

        {
            auto temp = temp_buf.get_access<sycl::access::mode::read_write>();
            parallel_sort(temp, temp + n1 + n2);

            auto arr_acc = arr.get_access<sycl::access::mode::write>();
            parallel_for(range<1>(n1 + n2), [=](id<1> idx) {
                arr_acc[left + idx] = temp[idx];
            });
        }
    }
}

int main() {
    int array_size = 100000;
    std::vector<int> a = generateRandomArray(array_size);
    auto start = std::chrono::high_resolution_clock::now(); // Get start time

    queue q;

    buffer<int, 1> arr_buf(a.data(), range<1>(a.size()));

    q.submit([&](handler &h) {
        mergeSort(q, arr_buf, 0, a.size() - 1);
    });

    q.wait_and_throw();

    auto arr_acc = arr_buf.get_host_access();

    auto end = std::chrono::high_resolution_clock::now(); // Get end time
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << 运行时间: " << duration.count() << "毫秒" << std::endl;
    return 0;
}

实验对比

(1)运行截图

实验通过随机生成不同长度的乱序vector (n),加速前后归并排序结果一致。下图展示n=100000时,基于oneAPI的SYCL归并排序的结果:

(2)效率对比

输入乱序数组长度(n)传统归并排序基于oneAPI的SYCL归并排序
10000065ms1871ms
100000006705ms13111ms
10000000071945ms117427ms

运行平台:Intel® DevCloud

注:

1、由于使用Devcloud平台,计算资源为动态分配,表中每组数据均为重复5次取平均数

2、由于平台差异,传统归并排序得到的计算时间或与其他平台(如本机)可能存在较大的差距,但不影响同平台下的加速效果比较。

实验总结

根据实验结果,我们可以看到,在输入规模相对较小的情况下(例如100000),传统归并排序比基于 oneAPI 的 SYCL 归并排序要快得多。这是因为 SYCL 归并排序需要额外的开销来处理并行计算。

然而,随着输入规模较大的情况下,SYCL 归并排序并没有表现出更好的性能优势。

可能存在SYCL加速的实现问题,需要进一步修改和验证。

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值