65.9K
CodeProject 正在变化。 阅读更多。
Home

培育并行标准:多样性、一致性和交叉授粉

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2023年12月14日

CPOL

5分钟阅读

viewsIcon

2092

本文强调了 Level Zero、SYCL、SPIR-V、OpenMP、ISO C++ 和 ISO Fortran 等各种标准之间互操作性的重要性,并探讨了过去十年这些标准的演变和一致性,同时还考虑了 SYCL 如何影响未来的

oneAPI 是一项开放的加速器编程规范(请参阅 oneapi.io),它包括一种语言(The Khronos Group 的 SYCL*)、标准化的库接口(用于神经网络、数据分析等)以及一种接近硬件的编程接口(Level Zero)。oneAPI 的设计宗旨是实现硬件和供应商无关性,并且已经成功地在多家供应商的 CPU、GPU、FPGA 和其他加速器上进行了演示。oneAPI 提供了一种基于标准的编程方法,使开发人员能够在不牺牲跨硬件和软件环境可移植性的前提下实现高绩效。

oneAPI 的实现得益于软件堆栈中众多互操作标准的集合。其中一些标准(如 Level Zero 和 SYCL 的扩展)是 oneAPI 规范的一部分。另一些标准(如 SYCL 和 SPIR-V)是为 oneAPI 提供可移植编程基础的行业标准。还有一些标准(如 OpenMP*、ISO C++ 和 ISO Fortran)描述了可叠加在 oneAPI 之上的加速器编程的替代语法。

所有这些标准之间的互操作性至关重要,因为大型应用程序很少由单个开发人员使用单一语言和单一抽象级别编写。更常见的是使用多种技术的混合,调用其他开发人员编写的库(例如,用 Fortran 和 OpenMP 编写的应用程序使用用 SYCL 编写的优化库)。保持互操作性需要持续的努力,因为不同的标准经常为新问题提供自己的解决方案。不同的标准可能针对不同的抽象级别,可能偏好不同的编码风格,并且必须始终考虑任何新功能如何与现有功能和既定约定相结合。但标准仍然可以相互学习,在术语上保持一致,并在适当的时候交叉授粉功能。

回顾 SYCL、C++ 和 OpenCL* 过去十年的发展,我们可以清楚地看到这种一致性和交叉授粉的过程(图 1)。例如,OpenCL 2.0 采用了 C++11 内存模型的概念和术语,并用作用域和多个地址空间的概念对其进行了扩展。OpenCL 2.0 的功能(如组和组函数)后来与 C++17 并行算法的某些方面相结合,生成了 SYCL 2020 的组算法库,使开发人员能够使用熟悉的 C++ 语法来访问硬件层次结构多个级别的供应商优化操作。

这是一个持续的过程,在我们最近的 IWOCL 论文 — “Towards Alignment of Parallelism in SYCL and ISO C++”(SYCL 和 ISO C++ 中的并行性一致性) — 中,我们提出了一些新的 SYCL 澄清,旨在弥合 SYCL 工作项和 C++17 执行线程等概念之间的差距。这些澄清使得更容易理解叠加在 SYCL 之上的 C++17 并行算法的行为,并且是允许某些 C++17 执行策略在 SYCL 设备上正确工作的必要步骤。

图 1. SYCL*、ISO C++ 和 OpenCL* 的并行演化

展望未来,我们看到了 SYCL 在 C++ 异构编程的未来中发挥影响的机会。我们相信,实现者使用 SYCL 的经验能够并且将直接融入到为 C++ 提出的新高级抽象的设计中。但我们也相信,SYCL 将在很长一段时间内继续在开发人员中发挥重要作用,通过与相关 C++ 抽象完全互操作的机制,直接访问更低级别和最先进的硬件功能。

这种未来可能是什么样子?虽然我们无法确定,但有一些 ISO C++ 提案提供了一些线索:P2300 (std::execution) 和 P2500(“C++ 并行算法和 P2300”)。如果这些提案被接受 — 最早也要到 2026 年! — 那么就可以编写出类似图 2 中的代码。

// Use SYCL to represent a specific “scheduler”
// NB: get_scheduler() doesn’t exist (yet)!
auto q = sycl::queue{sycl::gpu_selector_v};
scheduler auto sch = q.get_scheduler();

// Submit a parallel loop to the SYCL scheduler using P2300/P2500 features
std::for_each(std::execute_on(sch, std::execution::par_unseq), begin, end,
  [=](auto i) {
  ...
});
图 2. 混合了 C++ 并行算法和 SYCL* 以及 P2300/P2500 中提出的特性的未来一瞥

对于那些等不及 2026 年的人来说,现在已经可以使用 oneAPI DPC++ 库 (oneDPL) 将 C++ 并行算法和 SYCL 混合使用了。oneDPL 是一个开源库,它使 C++ 并行算法能够在 SYCL 设备上执行,为开发可在 SYCL 可用的任何地方执行的应用程序提供了一种高生产力的解决方案。oneDPL 还作为探索 ISO C++ 未来可能扩展的载体 — 已经有工作正在进行中,以标准化基于 C++20 ranges 的并行范围算法,并探索表示异步并行算法的方法。使用 oneDPL 将 C++ 并行算法提交到 SYCL 设备非常简单,可以像使用支持 SYCL 的执行策略一样简单(图 3)。使用 oneDPL 的实验性功能,通过利用延迟求值的视图来实现内核融合(图 4),可以释放出更高水平的性能和生产力。

// Submit a parallel loop to the default SYCL device using oneDPL
std::for_each(oneapi::dpl::execution::dpcpp_default, begin, end,
  [=](auto i) {
  ...
});
图 3. 使用 oneDPL 中现有功能混合 C++ 并行算法和 SYCL*
// Submit three algorithms to the default SYCL device as three kernels
using namespace oneapi::dpl;
reverse(execution::dpcpp_default, begin(data), end(data));
transform(execution::dpcpp_default, begin(data), end(data), begin(result),
          [](auto i){ return i * i; });
auto res = find_if(execution::dpcpp_default, begin(result), end(result),
                   pred);

// Submit a pipeline of range transformations to the default SYCL device,
// which will execute as a single kernel
using namespace oneapi::dpl::experimental::ranges;
auto res = find_if(execution::dpcpp_default,
                   views::all(sycl::buffer{data})
                   | views::reverse
                   | views::transform([](auto i){return i * i;}), pred);
图 4. 使用 oneDPL 的实验性功能混合 C++ ranges 和 SYCL*

oneDPL 的当前语法可能与最终的语法略有不同,但这仅仅是标准化过程在起作用。最终会包含在未来 C++ 标准中的任何内容,都将是多年工作的成果,借鉴了多个库和多个硬件供应商的经验,以建立共同的需求和最佳实践。

我们经常被问到开发人员是否应该用 OpenMP、SYCL、ISO C++ 或 ISO Fortran 来编写他们的程序。答案很简单:是的!这些都是异构编程的有效方法,每种方法都有其独特的优缺点,并且由于 Fortran 的 ISO_C_BINDING、OpenMP 的 interop 子句和 SYCL 的后端互操作性接口等功能,结合这些方法从未如此简单(图 5)。oneAPI 核心标准之间的强大互操作性让我们能够拥抱混合搭配的能力,使用最适合当前任务的工具组合。

图 5. 异构编程方法的比较:高级抽象提供了编写高性能程序的解决方案,提高了生产力;低级抽象为专家开发者提供了直接控制
© . All rights reserved.