文章目录
Hello,大家好。这里是DIONG的第一篇blog〃•ω‹〃
最近,我参加了一场由Intel公司组织的SYCL实验。在本次实验中,我熟悉了oneAPI和DevCloud平台的使用,并学习了SYCL编程的基本思路。本文将介绍oneAPI和Intel的DevCloud平台,分享我的学习过程和心得体会,并将通过SYCL编程实现矩阵乘法运算来比较使用CPU和GPU执行程序时的性能差异。希望能让读者更好地理解oneAPI和SYCL编程模型。
一、oneAPI概述
在并行计算领域,oneAPI作为一个重要的多平台编程模型,为开发者提供了巨大的便利。oneAPI是一个面向数据中心、边缘计算和集成系统的统一编程模型。它包括了一系列的编程语言、库和工具,旨在简化跨不同计算架构的开发工作。在本文中,我们将在Intel的DevCloud平台上,构建一个简单的SYCL程序来实现矩阵乘法运算。
二、设置环境
使用oneAPI的第一步是设置合适的开发环境。Intel的DevCloud是一个云端平台,提供了丰富的硬件和软件资源,非常适合进行此类实验。在DevCloud上,我们可以轻松访问和配置所需的硬件和软件环境。
链接:https://idzcn.com/devcloud.htm
三、SYCL编程简介
在DevCloud上的JupyterLab中,有详细的SYCL教程文档,本文仅列出一些重要的基本概念,以便初学者更好地理解SYCL编程。
1.什么是数据并行C++和SYCL?
数据并行C++(DPC++)是oneAPI对SYCL的实现。它基于现代C++的生产力优势和熟悉的构造,并融合了用于数据并行和异构编程的SYCL标准。SYCL是一个单一的源代码,其中可以混合主机代码(host code)和异构加速器内核(heterogeneous accelerator kernels)在同一个源文件中。SYCL程序在主机计算机上调用并将计算卸载到加速器上。程序员使用熟悉的C++和库构造,还增加了一些功能,如用于工作定位的队列(queue)、用于数据管理的缓冲区(buffer)以及用于并行性的parallel_for,以指导哪些部分的计算和数据应该被卸载。
2.基本概念
设备(Device)
设备类表示使用Intel® oneAPI工具包的系统中加速器的功能。设备类包含用于查询有关设备的信息的成员函数,这对于SYCL程序中创建多个设备的情况非常有用。
- 设备选择器(Device Selector):这些类允许根据用户提供的启发式方法来运行时选择执行内核的特定设备。以下代码示例显示了使用标准设备选择器(default_selector_v、cpu_selector_v、gpu_selector_v、accelerator_selector_v)的用法。
queue q(gpu_selector_v);
//queue q(cpu_selector_v);
//queue q(accelerator_selector_v);
//queue q(default_selector_v);
//queue q;
std::cout << "Device: " << q.get_device().get_info<info::device::name>() << "\n";
队列(Queue)
队列将命令组提交给SYCL运行时以执行。队列是一种机制,用于将工作提交到设备。一个队列映射到一个设备,可以将多个队列映射到同一个设备。
q.submit([&](handler& h) {
//COMMAND GROUP CODE
});
内核(Kernel)
内核类封装了在实例化命令组时在设备上执行代码的方法和数据。内核对象不是由用户显式构造的,而是在调用内核调度函数(例如parallel_for)时构造的。
q.submit([&](handler& h) {
h.parallel_for(range<1>(N), [=](id<1> i) {
A[i] = B[i] + C[i]);
});
});
- 选择设备内核运行的位置
工作被提交到队列,每个队列与一个设备关联(例如特定的GPU或FPGA)。您可以决定将队列与哪个设备关联(如果需要),并在异构系统中拥有任意数量的队列来分派工作。 - 并行内核
并行内核允许多个操作的实例并行执行。这对于卸载完全独立且无序的每个迭代的基本for循环的并行执行非常有用。并行内核使用parallel_for函数来表示。 - 基本并行内核
基本并行内核的功能通过range、id和item类公开。Range类用于描述并行执行的迭代空间,id类用于索引并行执行中的内核的单个实例。
缓冲模型(Buffer Model)
缓冲区(Buffers)在SYCL应用程序中封装了数据,跨设备和主机。访问器(Accessors)是访问缓冲区数据的机制。
主机访问器(Host Accessor)
主机访问器是使用主机缓冲区访问目标的访问器。它在命令组的范围之外创建,此数据将在主机上可用。这些用于通过构造主机访问器对象将数据同步回主机。缓冲区销毁(Buffer Destruction)是将数据同步回主机的另一种方法,本文将不展开讲述,读者可以自行探索。
3.ND RANGE KERNELS
基本并行内核是并行化for循环的简单方法,但不允许在硬件级别进行性能优化。ND-Range内核(ND-Range kernel)是另一种表达并行性的方式,它通过提供对本地内存的访问和将执行映射到硬件上的计算单元,从而实现低级性能调整。整个迭代空间被分成称为工作组的较小组,工作组中的工作项在硬件上的单个计算单元上调度。
将内核执行分组为工作组将允许控制资源使用和平衡工作分配。ND-Range内核的功能通过nd_range和nd_item类公开。nd_range类表示使用全局执行范围和每个工作组的局部执行范围的分组执行范围。nd_item类表示内核函数的单个实例,并允许查询工作组范围和索引。
h.parallel_for(nd_range<1>(range<1>(1024),range<1>(64)), [=](nd_item<1> item){
auto idx = item.get_global_id();
auto local_id = item.get_local_id();
// CODE THAT RUNS ON DEVICE
});