新用户注册入口 老用户登录入口

[转载]英特尔oneAPI——异构计算学习总结

文章作者:转载 更新时间:2023-07-22 10:28:50 阅读数量:320
文章标签:oneAPIDPC++异构计算SYCL多架构编程编程模型
本文摘要:英特尔oneAPI通过DPC++编程模型解决了多架构异构计算的挑战,提供了一种统一的解决方案。开发人员能够利用SYCL和USM(统一共享内存)在CPU、GPU和FPGA上编写高效并行代码,实现跨平台的数据并行处理。文中举例展示了如何使用oneAPI进行矢量加法操作,并详细阐述了如何处理Parallel kernel与queue类以实现并行执行及数据同步。此外,还探讨了事件依赖关系管理,通过wait()函数、depends_on()方法以及in_order队列属性确保任务间的正确执行顺序。通过一系列实例与练习,读者可以深入了解oneAPI在异构计算环境中的实际应用与优势。
转载文章

本篇文章为转载内容。原文链接:https://blog.csdn.net/MCKZX/article/details/127630566。

该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。

作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。

如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。

英特尔oneAPI——异构计算学习总结

  • oneAPI编程模型
    • 多架构编程面临的挑战
    • SYCL
      • DPC++
      • 编译和运行DPC++程序
    • 编程实例
      • 实现矢量加法
        • queue类
        • Parallel kernel
        • 通用的并行编程模板
        • Host Accessor
        • 矢量相加源代码
    • 统一共享内存 (Unified Shared Memory USM)
      • USM语法
      • 数据依赖
        • wait()
        • depends_on
        • in_order queue property
        • 练习1:事件依赖
        • 练习2:事件依赖
        • UMS实验

oneAPI编程模型

oneAPI编程模型提供了一个全面、统一的开发人员工具组合,可用于各种硬件设备,其中包括跨多个工作负载领域的一系列性能库。这些库包括面向各目标架构而定制化代码的函数,因此相同的函数调用可为各种支持的架构提供优化的性能。**DPC++**基于行业标准和开放规范,旨在鼓励生态系统的协作和创新。

多架构编程面临的挑战

在以数据为中心的环境中,专用工作负载的数量不断增长。专用负载通常因为没有通用的编程语言或API而需要使用不同的语言和库进行编程,这就需要维护各自独立的代码库。

由于跨平台的工具支持不一致,因此开发人员必须学习和使用一整套不同的工具。单独投入精力给每种硬件平台开发软件。

oneAPI则可以利用一种统一的编程模型以及支持并行性的库,支持包括CPU、GPU、FPGA等硬件等同于原生高级语言的开发性能,并且可以与现有的HPC编程模型交互。
无oneAPI的程序
有oneAPI的程序架构

SYCL

SYCL支持C++数据并行编程,SYCL和OpenCL一样都是由Khronos Group管理的,SYCL是建立在OpenCL之上的跨平台抽象层,支持用C++用单源语言方式编写用于异构处理器的与设备无关的代码。

DPC++

DPC++(Data Parallel C++)是一种单源语言,可以将主机代码和异构加速器内核写在同一个文件当中,在主机中调用DPC++程序,计算由加速器执行。DPC++代码简洁且效率高,并且是开源的。现有的CUDA应用、Fortran应用、OpenCL应用都可以用不同方式很方便地迁移到DPC++当中。
下图显示了原来使用不同架构的HPC开发人员的一些推荐的转换方法。
工作流

编译和运行DPC++程序

编译和运行DPC++程序主要包括三步:

  1. 初始化环境变量
  2. 编译DPC++源代码
  3. 运行程序
    例如本地运行,在本地系统上安装英特尔基础工具套件,使用以下命令编译和运行DPC++程序。
source /opt/intel/inteloneapi/setvars.sh
dpcpp simple.cpp -o simple
./simple

编程实例

实现矢量加法

以下实例描述了使用DPC++实现矢量加法的过程和源代码。

queue类

queue类用来提交给SYCL执行的命令组,是将作业提交到运算设备的一种机制,多个queue可以映射到同一个设备。

Parallel kernel

Parallel kernel允许代码并行执行,对于一个不具有相关性的循环数据操作,可以用Parallel kernel并行实现
在C++代码中的循环实现

for(int i=0; i < 1024; i++){a[i] = b[i] + c[i];
});

在Parallel kernel中的并行实现

h.parallel_for(range<1>(1024), [=](id<1> i){A[i] =  B[i] + C[i];
});

通用的并行编程模板

h.parallel_for(range<1>(1024), [=](id<1> i){
// CODE THAT RUNS ON DEVICE 
});

range用来生成一个迭代序列,1为步长,在循环体中,i表示索引。

Host Accessor

Host Accessor是使用主机缓冲区访问目标的访问器,它使访问的数据可以在主机上使用。通过构建Host Accessor可以将数据同步回主机,除此之外还可以通过销毁缓冲区将数据同步回主机。
buf是存储数据的缓冲区。

host_accessor b(buf,read_only);

除此之外还可以将buf设置为局部变量,当系统超出buf生存期,buf被销毁,数据也将转移到主机中。

矢量相加源代码

根据上面的知识,这里展示了利用DPC++实现矢量相加的代码。

//第一行在jupyter中指明了该cpp文件的保存位置
%%writefile lab/vector_add.cpp
#include <CL/sycl.hpp>
using namespace sycl;int main() {const int N = 256;//# 初始化两个队列并打印std::vector<int> vector1(N, 10);std::cout<<"\nInput Vector1: ";    for (int i = 0; i < N; i++) std::cout << vector1[i] << " ";std::vector<int> vector2(N, 20);std::cout<<"\nInput Vector2: ";    for (int i = 0; i < N; i++) std::cout << vector2[i] << " ";//# 创建缓存区buffer vector1_buffer(vector1);buffer vector2_buffer(vector2);//# 提交矢量相加任务queue q;q.submit([&](handler &h) {//# 为缓存区创建访问器accessor vector1_accessor (vector1_buffer,h);accessor vector2_accessor (vector2_buffer,h);h.parallel_for(range<1>(N), [=](id<1> index) {vector1_accessor[index] += vector2_accessor[index];});});//# 创建主机访问器将设备中数据拷贝到主机当中host_accessor h_a(vector1_buffer,read_only);std::cout<<"\nOutput Values: ";for (int i = 0; i < N; i++) std::cout<< vector1[i] << " ";std::cout<<"\n";return 0;
}

运行结果
在这里插入图片描述

统一共享内存 (Unified Shared Memory USM)

统一共享内存是一种基于指针的方法,是将CPU内存和GPU内存进行统一的虚拟化方法,对于C++来说,指针操作内存是很常规的方式,USM也可以最大限度的减少C++移植到DPC++的代价。
下图显示了非USM(左)和USM(右)的程序员开发视角。
在这里插入图片描述

类型 函数调用 说明 在主机上可访问 在设备上可访问
设备 malloc_device 在设备上分配(显式)
主机 malloc_host 在主机上分配(隐式)
共享 malloc_shared 分配可以在主机和设备之间迁移(隐式)

USM语法

  • 初始化:
    int *data = malloc_shared<int>(N, q);
    int *data = static_cast<int *>(malloc_shared(N * sizeof(int), q));
  • 释放
    free(data,q);

使用共享内存之后,程序将自动在主机和运算设备之间隐式移动数据。

数据依赖

使用USM时,要注意数据之间的依赖关系以及事件之间的依赖关系,如果两个线程同时修改同一个内存区,将产生不可预测的结果。

我们可以使用不同的选项管理数据依赖关系:

  • 内核任务中的 wait()
  • 使用 depends_on 方法
  • 使用 in_queue 队列属性

wait()

    q.submit([&](handler &h) {h.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 2; });}).wait();  // <--- wait() will make sure that task is complete before continuingq.submit([&](handler &h) {h.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 3; });});

depends_on

    auto e = q.submit([&](handler &h) {  // <--- e is event for kernel taskh.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 2; });});q.submit([&](handler &h) {h.depends_on(e);  // <--- waits until event e is completeh.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 3; });});

in_order queue property

    queue q(property_list{property::queue::in_order()}); // <--- this will make sure all the task with q are executed sequentially

练习1:事件依赖

以下代码使用 USM,并有三个提交到设备的内核。每个内核修改相同的数据阵列。三个队列之间没有数据依赖关系

  • 为每个队列提交添加 wait()
  • 在第二个和第三个内核任务中实施 depends_on() 方法
  • 使用 in_order 队列属性,而非常规队列: queue q{property::queue::in_order()};
%%writefile lab/usm_data.cpp
#include <CL/sycl.hpp>
using namespace sycl;static const int N = 256;int main() {queue q{property::queue::in_order()};//用队列限制执行顺序std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";int *data = static_cast<int *>(malloc_shared(N * sizeof(int), q));for (int i = 0; i < N; i++) data[i] = 10;q.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 2; });q.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 3; });q.parallel_for(range<1>(N), [=](id<1> i) { data[i] += 5; });q.wait();//wait阻塞进程for (int i = 0; i < N; i++) std::cout << data[i] << " ";std::cout << "\n";free(data, q);return 0;
}

执行结果
结果

练习2:事件依赖

以下代码使用 USM,并有三个提交到设备的内核。前两个内核修改了两个不同的内存对象,第三个内核对前两个内核具有依赖性。三个队列之间没有数据依赖关系

%%writefile lab/usm_data2.cpp
#include <CL/sycl.hpp>
using namespace sycl;static const int N = 1024;int main() {queue q;std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";//设备选择int *data1 = malloc_shared<int>(N, q);int *data2 = malloc_shared<int>(N, q);for (int i = 0; i < N; i++) {data1[i] = 10;data2[i] = 10;}auto e1 = q.parallel_for(range<1>(N), [=](id<1> i) { data1[i] += 2; });auto e2 = q.parallel_for(range<1>(N), [=](id<1> i) { data2[i] += 3; });//e1,e2指向两个事件内核q.parallel_for(range<1>(N),{e1,e2}, [=](id<1> i) { data1[i] += data2[i]; }).wait();//depend on e1,e2for (int i = 0; i < N; i++) std::cout << data1[i] << " ";std::cout << "\n";free(data1, q);free(data2, q);return 0;
}

运行结果
在这里插入图片描述

UMS实验

在主机中初始化两个vector,初始数据为25和49,在设备中初始化两个vector,将主机中的数据拷贝到设备当中,在设备当中并行计算原始数据的根号值,然后将data1_device和data2_device的数值相加,最后将数据拷贝回主机当中,检验最后相加的和是否是12,程序结束前将内存释放。

%%writefile lab/usm_lab.cpp
#include <CL/sycl.hpp>
#include <cmath>
using namespace sycl;static const int N = 1024;int main() {queue q;std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";//intialize 2 arrays on hostint *data1 = static_cast<int *>(malloc(N * sizeof(int)));int *data2 = static_cast<int *>(malloc(N * sizeof(int)));for (int i = 0; i < N; i++) {data1[i] = 25;data2[i] = 49;}//# STEP 1 : Create USM device allocation for data1 and data2int *data1_device = static_cast<int *>(malloc_device(N * sizeof(int),q));int *data2_device = static_cast<int *>(malloc_device(N * sizeof(int),q));//# STEP 2 : Copy data1 and data2 to USM device allocationq.memcpy(data1_device, data1, sizeof(int) * N).wait();q.memcpy(data2_device, data2, sizeof(int) * N).wait();//# STEP 3 : Write kernel code to update data1 on device with sqrt of valueauto e1 = q.parallel_for(range<1>(N), [=](id<1> i) { data1_device[i] = std::sqrt(25); });auto e2 = q.parallel_for(range<1>(N), [=](id<1> i) { data2_device[i] = std::sqrt(49); });//# STEP 5 : Write kernel code to add data2 on device to data1q.parallel_for(range<1>(N),{e1,e2}, [=](id<1> i) { data1_device[i] += data2_device[i]; }).wait();//# STEP 6 : Copy data1 on device to hostq.memcpy(data1, data1_device, sizeof(int) * N).wait();q.memcpy(data2, data2_device, sizeof(int) * N).wait();//# verify resultsint fail = 0;for (int i = 0; i < N; i++) if(data1[i] != 12) {fail = 1; break;}if(fail == 1) std::cout << " FAIL"; else std::cout << " PASS";std::cout << "\n";//# STEP 7 : Free USM device allocationsfree(data1_device, q);free(data1);free(data2_device, q);free(data2);//# STEP 8 : Add event based kernel dependency for the Steps 2 - 6return 0;
}

运行结果
在这里插入图片描述

本篇文章为转载内容。原文链接:https://blog.csdn.net/MCKZX/article/details/127630566。

该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。

作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。

如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。

相关阅读
文章标题:[转载][洛谷P1082]同余方程

更新时间:2023-02-18
[转载][洛谷P1082]同余方程
文章标题:[转载]webpack优化之HappyPack实战

更新时间:2023-08-07
[转载]webpack优化之HappyPack实战
文章标题:[转载]oracle 同时更新多表,在Oracle数据库中同时更新两张表的简单方法

更新时间:2023-09-10
[转载]oracle 同时更新多表,在Oracle数据库中同时更新两张表的简单方法
文章标题:[转载][Unity] 包括场景互动与射击要素的俯视角闯关游戏Demo

更新时间:2024-03-11
[转载][Unity] 包括场景互动与射击要素的俯视角闯关游戏Demo
文章标题:[转载]程序员也分三六九等?等级差异,一个看不起一个!

更新时间:2024-05-10
[转载]程序员也分三六九等?等级差异,一个看不起一个!
文章标题:[转载]海贼王 动漫 全集目录 分章节 精彩打斗剧集

更新时间:2024-01-12
[转载]海贼王 动漫 全集目录 分章节 精彩打斗剧集
名词解释
作为当前文章的名词解释,仅对当前文章有效。
oneAPI英特尔oneAPI是一套全面且统一的跨架构编程模型和工具集,旨在简化开发人员在CPU、GPU、FPGA等多种异构硬件平台上进行高性能应用开发的过程。它提供了一种基于C++的数据并行编程语言DPC++,以及一系列性能库,使得开发者能够以一种可移植性更强、更高效的方式编写代码,并实现跨平台的高性能计算。
DPC++ (Data Parallel C++)DPC++是一种基于C++的单源编程语言,是英特尔oneAPI的重要组成部分。通过DPC++,开发者可以使用统一的C++语法编写面向不同硬件架构(如CPU、GPU、FPGA等)的高性能并行代码。这种语言兼容OpenCL和SYCL标准,允许开发者在一个源文件中混合主机代码与设备内核,从而实现跨架构的无缝编程体验。
SYCLSYCL(Single-source heterogeneous programming model for OpenCL)是由Khronos Group管理的一种开放标准,为C++程序员提供了一个用于异构系统编程的单一源编程模型。SYCL允许开发者在C++代码中直接编写针对不同处理器(如CPU、GPU等)的并行任务,无需学习特定的设备编程语言,增强了代码的可读性和可维护性,同时也简化了多架构程序的设计与实现。
统一共享内存(Unified Shared Memory, USM)USM是英特尔oneAPI中的一个特性,它提供了一种虚拟化方法来统一CPU和GPU的内存视图,使开发者能够以指针方式在主机和设备之间自由地分配、访问和管理内存。通过USM,程序可以自动处理数据在主机与运算设备之间的迁移,极大地降低了编程复杂度和潜在的数据一致性问题,提高了异构环境下的编程效率。
延伸阅读
作为当前文章的延伸阅读,仅对当前文章有效。
在深入学习了英特尔oneAPI及其异构计算应用后,读者可能对当前行业动态、技术趋势以及更丰富的编程实践案例感兴趣。近期,英特尔持续推动oneAPI生态系统的建设与发展,不仅发布了新版的oneAPI工具包,其中包含了对DPC++和USM功能的增强与优化,还提供了更多针对特定领域的库支持,如深度学习、高性能计算等。
进一步探索异构编程的实际场景,可以参考《Parallel Computing》期刊中关于使用oneAPI进行高效科学计算的研究论文,作者通过实例展示了如何利用oneAPI跨平台特性,实现在多种硬件架构上无缝部署并行算法。此外,《ACM Transactions on Architecture and Code Optimization》上也有一篇关于oneAPI性能分析的文章,详细比较了其与CUDA、OpenMP等传统并行编程模型在不同硬件环境下的性能表现。
同时,关注GTC(GPU Technology Conference)等业界重要会议的最新报道,可了解包括英特尔在内的各大厂商在异构计算领域的最新研究成果和技术布局。例如,在最近的GTC大会上,有专家分享了基于oneAPI在自动驾驶、人工智能医疗影像处理等方面的应用案例,突显了oneAPI在实际项目中的强大适应性和灵活性。
对于开发者来说,参与Intel Developer Zone社区或访问GitHub上的oneAPI示例代码库是持续跟进和学习的最佳途径之一,这里不仅有详尽的教程指导,还有众多开发者分享的一线实践经验,帮助你更好地掌握oneAPI,并将其应用于解决复杂计算问题和应对未来挑战。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
fg %jobnumber - 将后台作业切换至前台运行。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
纯js实用T恤衫花纹图案预览特效 01-26 基于Bootstrap仿Github样式下拉列表框插件 08-08 jQuery电子邮件地址填写自动完成插件 04-30 Superset 数据源连接配置:精细化自定义SQLAlchemy URI实现数据分析与可视化,含SSL加密连接实例 03-19 jquery可任意拖动排序的导航图片效果 02-23 侧边栏个人图文简历HTML模板 12-09 Beego框架升级中的Bee工具版本兼容性问题与迁移策略:结构变更、功能接口变动及社区解决方案 12-07 Kibana无法启动:针对服务器内部错误的Elasticsearch连接、配置文件、端口冲突与资源排查解决(注:由于字数限制,未能完全包含所有关键词,但包含了核心问题描述及几个关键排查点) 11-01 ClickHouse外部表使用中文件权限与不存在问题的解决方案:错误提示、查询操作与文件路径管理实务 09-29 本次刷新还10个文章未展示,点击 更多查看。
Apache Atlas UI无法正常加载与样式丢失问题排查及解决方案:关注网络连接、浏览器缓存与开发者工具应用 09-25 Greenplum数据库中数据插入操作详解:单行多行插入与gpfdist实现大批量导入 08-02 [转载]html5 footer header,html-5 --html5教程article、footer、header、nav、section使用 07-16 [转载][GCC for C]编译选项---IDE掩盖下的天空 06-29 简洁大方珠宝钻石收藏网站模板下载 06-20 黑色高端精致汽车4s店美容html5模板下载 06-01 蓝色互联网项目融资管理平台网站模板 05-16 响应式游戏开发类企业前端cms模板下载 05-02 Beego框架动态路由实现:重定向与命令行参数驱动的路由设计实践 04-05 .NET 中字典操作避免 KeyNotFoundException:TryGetValue、ContainsKey 与 GetOrAdd 实践详解 04-04 [转载]2021/4/23爬虫第五次课(爬虫网络请求模块下下) 03-01
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"