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

跨架构编译:在 Intel oneAPI 编译器中使用 SYCL 代码

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2022 年 5 月 11 日

CPOL

6分钟阅读

viewsIcon

11847

downloadIcon

78

本文将深入探讨使用 Intel oneAPI DPC++/C++ 编译器进行面向跨架构部署的 SYCL 代码编译。

现代工作负载需要多样化的架构才能有效且高效地运行。每种架构都提供了特定的库、工具和编程模型。因此,要在不同设备上运行应用程序,我们需要重写部分代码。重写代码是一项耗时的工作,会阻碍我们专注于改进算法。

我们需要一种能够跨越架构界限的、富有成效、高性能且异构的编程模型。换句话说,我们希望拥有一个单一的代码库,能够透明地在所有设备上运行,而无需考虑硬件的架构,同时仍能提供最佳性能。这就是 oneAPI 的愿景,它是一项行业倡议,Intel 则提供了 oneAPI 规范的产品实现,并附有一系列全面的开发者工具包。Intel oneAPI 产品的一个关键组成部分是 Intel® oneAPI DPC++/C++ 编译器,其目标是让设备之间建立某种通用基础,以便软件能够无缝运行在异构系统上。

Intel 已经根据行业标准迭代完善了该编译器,并提供了参考实现。

SYCL 是一个基于 C++17 标准的、免版税的、单源嵌入式领域特定语言。它提供了一个抽象层,允许在异构架构上进行编程。基于 SYCL,该编译器可以将单个源文件中的现代 C++ 和 SYCL 代码同时编译为 CPU 以及各种加速器(如图形处理单元 (GPU) 和现场可编程门阵列 (FPGA))的代码。

本文演示了如何使用该编译器编译一个简单的 SYCL 应用程序,展示了即使是一个简单的 SYCL 程序也能轻松地在我们选择的多个设备上运行。我们还将回顾

  • SYCL 实现
  • 一个包含 OpenCV 库、Intel oneAPI Base Toolkit 以及运行代码所需的其他依赖项的 Dockerfile
  • 两个用于在 Linux 系统上一键构建和运行 Docker 容器的脚本

在开始本教程之前,请先了解 Intel oneAPI DPC++/C++ 编译器 的入门指南。该文档提供了对在不同平台上编译和执行更简单的 SYCL 程序的 P. 基础知识。

将 SYCL 代码与 Intel oneAPI DPC++/C++ 编译器结合使用

为了提供如何使用编译器编译 SYCL 代码的实践概述,我们将构建一个应用程序来增强下面 oneAPI Logo 的略微曝光不足的图像。我们将使用 SYCL 来提亮此图像。

让我们从理解我们的应用程序开始。我们的图像由 611x611 像素组成,每个像素有三个通道(红色、蓝色和绿色)。总共有超过一百万个值。为了提亮图像,我们将为每个值加 25。顺序实现将遍历图像中的每个值。

但这本身就是一个顺序问题吗?

答案是否定的。相反,此操作是高度并行的。每个像素代表一个独立的计算操作,可以独立且以任意顺序执行。因此,我们可以同时处理所有像素值。本教程将同时展示 C++ 串行实现和 SYCL 并行版本。

但首先,让我们使用 OpenCV 库加载图像以访问像素值。这段代码使用 cv::imread 函数给定图像路径,将图像加载到内存中。

std::string image_path = cv::samples::findFile("img_lowexposure.jpg");
cv::Mat img = cv::imread(image_path, cv::IMREAD_COLOR);

现在,让我们按顺序处理像素。

for(int i = 0; i < img.rows; ++i){
    for(int j = 0; j < img.cols; ++j){
        for(int c = 0; c < img.channels(); ++c){
          img.at<cv::Vec3b>(i,j)[c] = std::clamp(img.at<cv::Vec3b>(i,j)[c] + 25, 0,  255);}}}

C++ 实现的缺点在于它是串行的。它使用了三个嵌套的 for 循环,这些循环遍历了所有像素及其通道,因为编译器端没有自动优化。请注意,我们对值进行了限制,以避免图像过曝。

SYCL 中的等效算法实现非常简单,并且可以利用底层的并行设备。我们可以使用设备选择器轻松地定位特定架构。

device d;
try{
    d = device(gpu_selector());
} catch (exception const& e){
    std::cout << "Cannot select a GPU\n" << e.what() << "\n";
    std::cout << "Using a CPU device\n";
    d = device(cpu_selector());}
std::cout << "Device: "<< q.get_device().get_info<info::device::name>() << std::endl;

首先,我们尝试选择一个 GPU 设备(如果可用)。如果不可用,我们则使用 CPU。我们将选定的设备与一个创建设备连接的队列关联起来。

现在,我们可以使用 submit 函数轻松地将工作提交到队列。但在那之前,我们需要将图像封装到缓冲区中。

queue q(d);
buffer<uint8_t, 3> frame_buffer(img.data, range<3>(img.rows, img.cols, 3));

然后,我们将一个命令组函数对象提交给队列。这需要一个命令组处理器作为参数。命令组函数对象封装了对缓冲区的访问器——在我们的例子中是读写访问器——以及内核。

q.submit([&](handler& cgh){
    auto pixels = frame_buffer.get_access<access::mode::read_write>(cgh);
    cgh.parallel_for(range<3>(img.rows, img.cols, 3), [=](item<3> item){
        uint8_t p = pixels[item];
        pixels[item] = sycl::clamp(p + 25, 0, 255);});
});

在上面的代码中,我们排队了一个 parallel_for 任务,并传递了一个由每个工作项执行的函数。range 类决定了迭代空间,而 item 类是内核函数的单个实例。内核操作于单个像素。我们运行与图像中像素数量相同的内核,以同时处理所有像素。内核以并行方式执行。

最后,在输出最终图像之前,我们让程序等待内核工作完成,如下所示:

q.wait_and_throw();
cv::imwrite("img_sycl.jpg", img);

我们已准备好编译代码。但在此之前,我们必须通过 sourcing setvars 来设置环境变量。

source /opt/intel/oneapi/setvars.sh

为了编译我们的代码,我们使用 Intel oneAPI DPC++/C++ 编译器。

我们使用 dpcpp 命令,如下所示:

dpcpp src.cpp -std=c++17 -I/usr/local/include/opencv4 -lopencv_core -lopencv_imgcodecs -lopencv_highgui -o src

包含 OpenCV 头文件目录并在编译时指定所有必需的库非常重要。

执行应用程序后,检查结果。它们应该与下图相同。左边是 oneAPI Logo 的曝光不足的图像,右边是同一图像的亮度已增加。

 

恭喜!您已经实现了一个用于提亮图片的 SYCL 应用程序。

用例和好处

鉴于硬件多样性的爆炸式增长,SYCL 旨在实现完全的异构编程。它最大限度地减少了样板代码,并使并行性显而易见。最大限度地减少代码并使并行性显而易见可以简化代码迁移并提高我们的生产力,因为我们现在可以专注于改进算法,而不是为了在新硬件上运行而重写代码。

Intel oneAPI DPC++/C++ 编译器支持跨多种架构的快速、高效的异构编程。异构编程允许开发人员轻松选择专用设备来加速其工作流的特定部分。例如,我们可以使用 GPU 和 FPGA 来加速我们应用程序中的图像处理,同时仍使用 CPU 来处理串行任务。这种编程模型可以使我们的应用程序在不更改代码库的情况下运行在新加速器上,同时确保最佳性能。

结论

通过使用 Intel 的 oneAPI DPC++/C++ 编译器,我们通过这个单一程序实现了 SYCL 代码的异构计算优势,并提高了图形的亮度。现在,我们可以将代码部署到 CPU、GPU 等设备上,以利用我们拥有的计算能力。

首先,我们用 C++ 实现了一个应用程序。然后,通过 SYCL,我们实现了异构计算,突出了我们的 SYCL 代码如何直观地表达并行性。

在设置了编译器的环境变量之后,我们使用该编译器编译了 SYCL 程序,并包含了一些用于图像处理的 OpenCV 功能。现在,我们可以透明地定位多个设备来加速我们的应用程序。

有兴趣了解更多信息吗?探索 oneAPI 工具包提供的针对特定工作负载的优化库、调试工具和高级分析功能。而且,为了快速开始而无需下载工具包,可以尝试 Intel DevCloud

© . All rights reserved.