将C++Thrust*应用迁移至SYCL*和oneDPL——使用 oneAPI 革新与加速 C++ 应用
英特尔技术汇
2023年04月04日 10:00

作者:Pablo Reble

程序员总是喜欢使用库,这不仅因为我们想偷懒,更是因为这些库有助于提高生产力和性能。一旦目标硬件发生变化,就要推倒重来,我们为什么要浪费时间优化常用的模式呢?既然您已经识别出了模式,为什么不使用预定义抽象库,比如 ISO C++ 标准模板库 (STL)?这些库都经过专家的优化,可以让我们直接使用。

如果您只以 CPU 为目标的话,STL 是个不错的选择,但是我们的计算环境通常是异构的。因此,您需要其他选择来应对不止于CPU的异构环境。NVIDIA* Thrust 库和 oneAPI 数据并行 C++ 库 (oneDPL) 就是两个典型的选项。它们有什么不同之处?虽然这两个库都是开源项目 [1][2],但是 Thrust 的设备支持依赖专有软件组件(即只能通过 CUB*/CUDA* 支持 GPU),而这将引起厂商锁定。另一方面,oneDPL 基于 SYCL,SYCL 的设计理念是支持来自不同厂商的加速器[3]。

人们不禁会问,从 Thrust 迁移至非专有 oneDPL 的最佳方法是什么?解决这个问题需考察两种互补的迁移策略,一个是工具辅助选项,另一个是手动重新编码,本文会把它们应用到两个简单的示例中。您可能已经听说过 SYCLomatic 工具可用于从 CUDA 到 SYCL 的代码迁移[4]。工具辅助的源代码到源代码迁移方法有自身的局限性,生成的代码可能存在功能、质量和可维护性方面的问题,对模板化代码来说尤为严重。因此,通常需要进行手动编辑与代码现代化改造;例如,引入较新的 C++ 特性(如 lambda 函数)或自动类型推导。无论采用何种方式,迁移完成后,生成的代码将基于开放标准,并且与现代 C++ 保持一致。Thrust 已经问世十多年了,是时候升级您的代码了。我们来看一下相关的代码示例。

第一个示例是就地转换序列中的某些值。使用掩码定义需转换的值。函数接收两个输入流:一个用于保存值,另一个保存 stencil 序列。如果来自第二个输入序列的 stencil 值满足特定标准(在示例代码中是一个非零谓词),来自第一个序列的每个值都将被转换(在示例代码中是否定):

oneDPL 与标准 C++ 算法高度一致。然而,STL 和 oneDPL 都不支持 stencil 过载。该模式必须以另一种方式表示,因此 SYCLomatic 提供了一种直接替代版本的函数:

更好的方法是将 lambda 函数用作自定义函子,从而创建标准 C++ 代码。借助该方法,将 stencil 序列作为通用输入序列,以不同的方式处理。谓词评估在自定义函子内完成,而不是隐藏在库的内部:

现在,除了针对 SYCL 设备所需的自定义执行策略之外,我们专门使用 STL 行为表达了模式。这是 Thrust 和 oneDPL 的一个重要区别。Thrust 有一个默认模式,由输入迭代器定义在哪里执行算法。ISO C++ 与之不同,它是由执行策略(而非迭代器)定义执行参数。oneDPL 与标准 C++ 算法高度一致,并提供了自定义策略。您可以用它来定义在哪里执行算法。为了方便起见,还有一个只针对默认 SYCL 设备的预定义策略。这两个策略被上述示例代码的阶段 1 和 2 所采用。

下一个迁移示例着眼于另一种常见模式:排序。Thrust 版本按照键进行排序。该算法接收两个输入序列:一个包含值,另一个保存键:

通过比较键序列的元素,对输入值序列进行排序。重申一下,SYCLomatic 提供了替代函数,因为现有的 ISO C++ 函数中没有该模式:

自定义迭代器和函子可以帮助我们更上一层楼。我们使用 zip 迭代器将键和值序列绑定在一起,并创建单个迭代空间。我们的自定义函子在键值对上运行,仅比较关键元素。虽然结果略有冗余,但是它仅使用包含通用扩展的 ISO C++ 函数,例如自定义迭代器:

加速实现 C++ STL 算法的库可显著提高开发者的生产力。工具辅助的迁移步骤与手动代码编辑优势互补,共同提升了可维护性和代码质量。在未来的开发工作中,基于范围的 API 和 P2300R5 执行提案 [5] 作为可行的替代方案,将克服本文所述的当前 C++ 限制以及表示并行模式。然而,ISO C++ 还没有实现该目标,这些特性需要对现有应用进行重大调整。目前来看,为避免专有语言和厂商锁定,确保能够访问多厂商硬件,最好的方法是抓住一切机会开发 ISO C++,并保证扩展的通用性。

References

1. oneDPL 资料库: https://github.com/oneapi-src/oneDPL

2. Thrust 资料库: https://github.com/NVIDIA/thrust

3. SYCL案例: 为什么 ISO C++ 对异构计算来说还不够?  

4. SYCLomatic 资料库: https://github.com/oneapi-src/SYCLomatic. 请注意,本文中SYCLomatic的例子仅仅是起到演示的作用。诸如Intel DPC++ Compatibility Tool此类工具的实际应用结果可能会有所不同。

5. P2300R5 std::execution