新的硬件加速器不断涌现。这意味着开发人员希望(坦率地说需要)利用各种新硬件环境的创新。
软件执行流程采用更多不同的架构;任务在这些架构中被分叉、派生和合并。
在最简单和最常见的场景中,以矩阵乘法为中心的工作负载被并行化并卸载到专用 GPU。然而,已经有相当多的工作负载利用了多个不同的并发计算节点。2021 年,Evans Data Corporation 的一份报告1 发现,40% 的开发人员已经针对使用多种类型的处理器、处理器内核或协处理器来执行给定功能的异构系统。
直到最近,大多数开发人员都被锁定在特定的架构中,导致需要重新编码应用程序并为他们所针对的每个架构使用不同的库。通常,由于在不同架构上获得相同功能所需的代码返工量,性能会受到影响。
打破这些障碍的是oneAPI开放、基于标准、多架构、多供应商的编程模型和互补的英特尔® oneAPI 工具包,专门设计用于使用跨架构保持一致的 C++/SYCL* 抽象层和性能库 API。
使用 oneAPI,软件开发范式的变化以及针对给定工作负载的多个执行环境的目标和维护变得更容易实现;开发人员可以在不同的架构中重复使用他们的代码,同时仍然可以利用 CPU、GPU 和 FPGA 硬件加速。
为了帮助加快跨平台应用程序的开发时间和性能,英特尔正在通过集成 SYCL 和 oneAPI 跨平台、基于标准的技术来增强和扩展其性能库。
借助英特尔® oneAPI DPC++/C++ 编译器(一种实现 C++、SYCL 和 OpenMP* 的跨平台编译器)及其丰富的性能库系列、英特尔® oneAPI 产品和底层 oneAPI 开放行业规范,锚定在 LLVM 项目中,提供对发展社区的三大好处:
- 消除专有锁定
- 实现每个平台的所有硬件价值
- 能够快速正确地开发高性能代码
所有这些都可以完全集成到常见的集成开发环境中,例如 Microsoft Visual Studio*、Microsoft VS Code* 和 Eclipse*。
图书馆的跨架构势在必行
让我们把注意力转向 HPC、IoT 和 AI。
在异构环境中,这些用例越来越受欢迎,越来越多的加速器针对它们及其挑战。开发人员需要能够利用这种日益多样化的硬件。不清楚的是不支持跨环境代码的成本。没有可移植性,即使是最好的代码也会被遗忘。锁定在单一架构中的代码将无法利用未来的硬件进步,因此与更便携的替代方案失去了相关性。
跨平台开发的真正障碍是缺乏可以抽象架构差异的统一库集,使开发人员能够在这些不同的架构中重用他们的代码。通过正确的库集,他们可以根据性能、价格、可用性和/或任何其他因素选择平台,而不是屈从于“好吧,这就是我当前的代码运行的平台”。
oneAPI 的性能库系列为实现这种选择自由提供了答案。它们提供基于 API 的跨架构编程以加快上市时间,同时保持或超过单独使用 C++ 或 SYCL 实现的性能。
英特尔® oneAPI DPC++ 库 (oneDPL)
oneDPL是这种新的、通用的、基于 API 的软件开发方法的基础。它通过提供基于熟悉标准的高生产力 API 来补充英特尔 oneAPI DPC++ 编译器,包括以下标准,以最大限度地提高 CPU、GPU 和 FPGA 的生产力和性能:
- C++ 标准模板库 (STL),已针对 SYCL 内核进行了验证
- 具有在 SYCL 设备架构上运行的实施策略的并行 STL (PSTL) 算法
- Boost.Compute
- SYCL
要选择特定的目标架构,只需将设备选择器传递给旨在并行卸载到 SYCL 队列 sycl::queue 的任务。
有一系列可以直接使用的预定义设备选择器:
默认选择器 | 如果找不到设备,则根据实现定义的启发式选择设备或主机设备 |
gpu_selector | 选择 GPU |
加速器选择器 | 选择加速器 |
cpu_selector | 选择 CPU 设备 |
主机选择器 | 选择主机设备 |
(可以在设备选择器运算符的帮助下从这些设备中派生新设备。请查看oneAPI和SYCL规范以了解详细信息。)
使用设备选择器就像下面的例子一样简单:
// Standard SYCL header
#include <CL/sycl.hpp>
int main() {
sycl::device d;
// Exception checking for GPU availability
try {
d = sycl::device(sycl::gpu_selector());
} catch (sycl::exception const &e) {
std::cout << "Cannot select a GPU\n" << e.what() << "\n";
std::cout << "Using a CPU device\n";
d = sycl::device(sycl::cpu_selector());
}
std::cout << "Using " << d.get_info<sycl::info::device::name>();
}
在不重新设计代码的情况下利用这种方法的最佳方式,并为未来的软件架构更改提供工程投资,是在成熟的高性能库上构建您的工作负载软件堆栈,这些库具有经过验证的跟踪记录采用最新的技术进步。
值得注意的是,英特尔已经开放了其庞大的 API 库,并将其中的许多 API 贡献给了跨平台的 oneAPI 规范。这也支持为其他非英特尔处理器架构开发 oneAPI 库。具体来说,oneAPI 零级开放了底层编程模型,以包含 3 rd方设备供应商和开放的开发人员社区参与。
英特尔® oneAPI 线程构建模块 (oneTBB)
oneTBB与 oneDPL 一样,基于成熟的符合 C++ 标准的类,并结合了最新的Parallel STL,这是一种支持并行执行策略的 C++ 标准库算法的实现。
因此,它是 oneAPI 生态系统的一部分,与 oneDPL 共存并通过灵活的性能库增强其功能,从而简化了向复杂应用程序添加并行性的工作。凭借其简化跨内核和架构的并行代码执行流程优化的能力,oneTBB 广泛用于工业和研究中的许多计算密集型领域——数值天气预报、海洋学、天体物理学、基因工程、地震勘探、自动化能源勘探, 和社会经济学。
oneTBB 不是将程序分解为功能块并为每个功能块分配一个单独的线程,而是强调数据并行编程,使多个线程能够处理集合的不同部分。通过将集合分成更小的部分,这可以很好地扩展到更多数量的处理器 - 随着处理器和加速器的添加,程序性能会提高。
这种设计原则允许库与其他线程包无缝兼容,从而能够与 oneAPI 以及遗留代码直接集成。
英特尔® oneAPI 数学内核库 (oneMKL)
oneMKL也是oneAPI 的一部分,是英特尔® 数学核心函数库的延续,它是自 2015 年以来业界使用最广泛的数学库1。它涵盖了数学、科学和数据分析中使用的广泛功能,包括线性代数、快速傅立叶变换、矢量随机数生成、统计、矢量数学和复杂几何(图 2)。
oneMKL 针对跨架构性能进行了优化,以支持可在多个架构(CPU、GPU 等)上运行的复杂数学处理例程,极大地简化了将数学函数卸载到 GPU 的过程。
为以下主要计算领域的关键功能添加了针对 CPU 和 GPU 架构进行优化的新 SYCL 接口:
- BLAS 和 LAPACK 密集线性代数例程
- Sparse BLAS 稀疏线性代数例程
- 随机数生成器 (RNG)
- 向量数学 (VM) 例程,用于优化向量上的数学运算
- 快速傅里叶变换 (FFT)
此外,结合 SYCL 接口,OpenMP 卸载可用于在英特尔® GPU 上运行标准 oneMKL 计算。
要利用 oneMKL 和 oneAPI SYCL 接口,只需首先包含两个头文件:
// Standard SYCL header
#include <CL/sycl.hpp>
// Declarations for Intel oneAPI Math Kernel Library SYCL/DPC++ APIs
#include "oneapi/mkl.hpp"
. . .
// Create a queue on the default device.
sycl::queue device_queue{sycl::default_selector{}};
std::cout << "Device: "
<< device_queue.get_device().get_info<sycl::info::device::name>()
<< std::endl;
// Allocate shared memory for matrices.
auto A = sycl::malloc_shared<double>(m * k, device_queue);
auto B = sycl::malloc_shared<double>(k * n, device_queue);
auto C = sycl::malloc_shared<double>(m * n, device_queue);
auto C_reference = (double *) calloc(m * n, sizeof(double));
. . .
// Call GEMM to do matrix multiplication, asynchronously
std::cerr << "Launching oneMKL GEMM calculation..." << std::endl;
oneapi::mkl::blas::row_major::gemm(device_queue, transA, transB, m, n, k,
alpha, A, lda, B, ldb, beta, C, ldc);
. . .
然后在您定义的给定 SYCL 队列的上下文中完全像以前一样使用 oneMKL 库函数调用。除此以外,无需更改任何内容,以确保您的库在未来适用于即将推出的 GPU 和加速器。