基于Intel oneAPI的图像卷积并⾏加速
问题描述
使用基于oneAPI的C++/SYCL实现一个用于计算图像的卷积操作。输⼊为一个图像矩阵和一个卷积核矩阵,输出为卷积后的图像。
技术方案
本项目实现了图像卷积的并行加速算法,其核心在于利用Intel oneAPI中的SYCL框架来实现高效的图像处理。图像卷积是一种常用于图像处理的技术,它通过一个卷积核与图像进行元素级乘法和累加,从而实现各种图像滤波效果。该算法的关键步骤包括:
-
缓冲区设置:使用SYCL中的buffer对象来存储输入图像、输出图像和卷积核。这些缓冲区负责管理内存和提供对数据的访问。
-
并行执行:利用Intel oneAPI提供的
queue.submit
和parallel_for
实现并行计算。这里,每个图像像素的卷积计算都作为一个独立的工作项来处理。这样可以充分利用现代硬件的并行处理能力,尤其是在处理大型图像时。 -
边界处理:在卷积过程中,对图像边界进行了特别处理,以确保卷积核不会超出图像边界。
-
数据访问:使用accessor对象来访问缓冲区中的数据。这种方法可以提高数据访问的效率,并保证数据的同步和一致性。
-
卷积核应用:卷积核在每个像素位置上应用,对应位置的像素和卷积核中的值进行乘法运算后累加,得到输出图像的相应像素值。
在实现并行的卷积算法前,我先实现了一个朴素的串行卷积算法,比较两个算法的输出结果即可验证并行算法的正确性,同时可以评测并行算法的加速比。
核心算法如下:
void convolve(sycl::queue& queue,
const std::vector<float>& inputImage,
std::vector<float>& outputImage,
const std::vector<float>& kernel) {
sycl::range<2> imageSize(imageHeight, imageWidth);
sycl::buffer<float, 2> bufferInput(inputImage.data(), imageSize);
sycl::buffer<float, 2> bufferOutput(outputImage.data(), imageSize);
sycl::buffer<float, 2> bufferKernel(kernel.data(), sycl::range<2>(kernelSize, kernelSize));
queue.submit([&](sycl::handler& cgh) {
auto accessorInput = bufferInput.get_access<sycl::access::mode::read>(cgh);
auto accessorOutput = bufferOutput.get_access<sycl::access::mode::write>(cgh);
auto accessorKernel = bufferKernel.get_access<sycl::access::mode::read>(cgh);
cgh.parallel_for<class convolveKernel>(imageSize, [=](sycl::id<2> idx) {
size_t x = idx[0];
size_t y = idx[1];
float sum = 0.0f;
for (size_t i = 0; i < kernelSize; ++i) {
for (size_t j = 0; j < kernelSize; ++j) {
size_t ix = x + i - kernelSize / 2;
size_t iy = y + j - kernelSize / 2;
if (ix < imageHeight && iy < imageWidth) {
sum += accessorInput[ix][iy] * accessorKernel[i][j];
}
}
}
accessorOutput[x][y] = sum;
});
});
queue.wait();
}
性能测试与验证
对于作业的运行环境,我使用了Intel DevCloud免费提供的具有oneAPI环境的Jupyter Lab(https://jupyter.oneapi.devcloud.intel.com/)。
Intel Devcloud Jupyter Lab提供持久化存储,并且集成了Intel提供的各种开发套件环境,和许多示例教程,亦提供免费的计算队列资源,可以直接在浏览器中访问开发环境完成开发,包括Intel CPU、GPU等均可免费使用,无需本地配置任何环境。
登陆Intel Devcloud Jupyter Lab后,新建终端并通过如下脚本编译并运行作业程序:
#!/bin/bash
source /opt/intel/oneapi/setvars.sh > /dev/null 2>&1
/bin/echo "##" $(whoami) is compiling SYCL_Essentials Module1 -- oneAPI Intro sample - 1 of 1 homework3.cpp
icpx -fsycl lab/homework3.cpp
if [ $? -eq 0 ]; then ./a.out; fi
直接分别运行一个小矩阵和卷积核的计算结果,比较是否一致判断正确性。
运行结果如下:
可见卷积的计算结果是正确的。
使用DevCloud Jupyter Notebook下的CPU,对一个1024*1024的矩阵,和128*128的卷积核进行卷积,计算加速比,运行结果如下:
可见使用了Intel oneAPI进行并行加速后,在图像尺寸较大的情况下,加速比高达37。
学习心得
在这门课程中,我通过实际操作深入理解了Intel oneAPI的核心概念和优势。oneAPI提供了一个统一的编程模型,用于跨多种硬件(如CPU、GPU、FPGA)构建高性能应用,这一点在目前的高性能计算框架中难能可贵。
oneAPI强大之处在于其支持异构计算。在此作业中,我利用了oneAPI的SYCL扩展,它提供了一个标准的C++编程模型,简化了在不同类型的处理器上编程的复杂性,且算法本身可以在不进行任何更改的情况下,支持异构计算,这种跨平台兼容性对于降本增效、优化性能至关重要。我使用了oneAPI中的并行编程工具来加速矩阵乘法。这个过程让我体会到了并行化对于提高大规模计算任务效率的重要性,且即便是Intel Devcloud Jupyter Notebook中提供的CPU资源,在应用并行加速后加速比也十分优秀。
通过使用SYCL和一系列API,我更深入地理解了如何将问题分解为可以并行处理的小任务。这种思维方式不仅对于使用oneAPI,而且对于现代高性能计算领域都是极其重要的。在开发过程中,我也学会了使用Intel DevCloud的工具进行调试和性能分析。这些工具帮助我识别并优化了代码中的瓶颈,这对于编写高效的并行程序至关重要。
最后,感谢Intel Devcloud提供的免费计算资源和环境,可以让我快速上手直接进行实验,无需进行任何环境配置,或购买任何特定硬件即可在真实的应用场景中进行实践,欢迎大家也来体验。