至强Phi上的OpenCL:2D卷积体验-OpenCL与OpenMP

至强Phi上的OpenCL:2D卷积体验-OpenCL与OpenMP,opencl,openmp,convolution,xeon-phi,Opencl,Openmp,Convolution,Xeon Phi,在opnecl中,以2D卷积为基准的Xeon Phi的性能似乎比openmp实现要好得多,即使是启用编译器的矢量化。Openmp版本在phi本机模式下运行,计时只测量计算部分:For循环。对于opencl实现,计时也仅用于内核计算:不包括数据传输。OpenMp enbaled版本使用2,4,60120240个线程进行测试240个线程为平衡的线程关联设置提供了最佳性能。但Opencl的性能甚至比源代码中带有pragma-enbled矢量化的240线程openmp基线要好17倍左右。输入图像大小为1

在opnecl中,以2D卷积为基准的Xeon Phi的性能似乎比openmp实现要好得多,即使是启用编译器的矢量化。Openmp版本在phi本机模式下运行,计时只测量计算部分:For循环。对于opencl实现,计时也仅用于内核计算:不包括数据传输。OpenMp enbaled版本使用2,4,60120240个线程进行测试240个线程为平衡的线程关联设置提供了最佳性能。但Opencl的性能甚至比源代码中带有pragma-enbled矢量化的240线程openmp基线要好17倍左右。输入图像大小为1024x1024到16384x16384,过滤器大小为3x3到17x17。在调用运行中,opencl优于openmp。这是opencl的预期加速吗??好得难以置信

编辑:

编译(openmp)

来源(convale.cpp):


*原始内核执行时间。不包括PCI总线上的数据传输时间。

英特尔的OpenCL实现将使用他们称之为“隐式矢量化”的方法,以利用矢量浮点单元。这涉及将工作项映射到SIMD通道。在您的示例中,每个工作项处理一个像素,这意味着每个硬件线程将使用Xeon Phi的512位向量单元一次处理16个像素

相比之下,OpenMP代码是跨像素并行的,然后在像素内对计算进行矢量化。这几乎可以肯定是性能差异的来源

为了让ICC以类似于隐式矢量化OpenCL代码的方式对OpenMP代码进行矢量化,您应该从最里面的循环中删除
#pragma ivdep
#pragma vector aligned
语句,而只需在水平像素循环前面放置一个
#pragma simd

#pragma omp parallel for num_threads(nNumThreads)
for (int yOut = 0; yOut < nHeight; yOut++)
{
    const int yInTopLeft = yOut;

    #pragma simd
    for (int xOut = 0; xOut < nWidth; xOut++)
    {
用于num_线程(nNumThreads)的pragma omp parallel for(int-yOut=0;yOut 当我使用ICC编译此文件时,它会报告它正在成功地对所需的循环进行矢量化。

之前:(对于最内部的循环,使用
#pragma ivdep
#pragma vector aligned
):

根据@jprice的建议(在水平数据上使用#pragma simd):

OpenMP现在比以前的执行速度快了2.8倍。现在可以与OpenCL进行公平的比较了! 感谢jprice和所有做出贡献的人。从你们身上汲取了巨大的教训

编辑: 以下是我的结果和比较:

            image   filter  exec Time (ms)
OpenMP  2048x2048   3x3     4.3
OpenCL  2048x2048   3x3     1.04

Speedup: 4.1X

OpenCL确实比OpenMP快?

您的OpenMP程序对一行图像使用一个线程。同一行中的像素是矢量化的。这等于您在OpenCL中有一个一维工作组。每个工作组处理一行图像。但在您的OpenCL代码中,似乎有一个二维工作组。每个工作组(映射到phi上的一个线程)正在处理一块图像,而不是一行图像。缓存命中率将不同。

您使用的是两组不同的代码,它们可能经过不同的优化。您是否自己编写了OpenMP?发布代码。它可能未经优化。OpenCL是否使用向量类型(例如float4)?这些将使用SSE/AVX。OpenMP仅负责线程,如果您想使用SSE/AVX,您必须自己完成。此外,Xeon Phi有自己的SIMD(AVX512)OpenCL可以利用512位宽。OpenMP不会为您这样做。Intel很可能对某些内核执行水平矢量化,这意味着单个Xeon Phi内核实际上可以在单个内核上同时运行16个线程(假设一个线程在32位VAL上运行),每个向量元素一个。这自然取决于内核,并且不是所有的都可以轻松地修改为这种处理方式。opencl和openmp基准是来自同一源的相同代码:AMD。opencl代码不是手动分解的——纯标量代码。框架进行向量化。对于openmp编译,编译器报告所有循环都是矢量化的。(--vec-report2)。即使二进制文件包含编译器报告的矢量指令,代码也可能不使用SSE/AVX吗?是的,Opencl可以轻松利用SIMD单元。如何确保openmp做到这一点(使用pragmas在我的代码中指定的编译器提示启用)?我正在将代码添加到编辑帖子中。您也可以尝试在x和y外部循环上进行并行化。这对于非常大的图像并不重要,但对于240个线程上的1024x1024,可能会。您必须对编译器进行一个小的更改,以接受以下内容:#pragma omp parallel for collapse(2)num_threads(nNumThreads)for collapse(int-yOut=0;yOut __kernel void Convolve(const __global float * pInput, __constant float * pFilter, __global float * pOutput, const int nInWidth, const int nFilterWidth) { const int nWidth = get_global_size(0); const int xOut = get_global_id(0); const int yOut = get_global_id(1); const int xInTopLeft = xOut; const int yInTopLeft = yOut; float sum = 0; for (int r = 0; r < nFilterWidth; r++) { const int idxFtmp = r * nFilterWidth; const int yIn = yInTopLeft + r; const int idxIntmp = yIn * nInWidth + xInTopLeft; for (int c = 0; c < nFilterWidth; c++) { const int idxF = idxFtmp + c; const int idxIn = idxIntmp + c; sum += pFilter[idxF]*pInput[idxIn]; } } const int idxOut = yOut * nWidth + xOut; pOutput[idxOut] = sum; }
            image filter  exec Time (ms)
OpenMP  2048x2048   3x3   23.4
OpenCL  2048x2048   3x3   1.04*
#pragma omp parallel for num_threads(nNumThreads)
for (int yOut = 0; yOut < nHeight; yOut++)
{
    const int yInTopLeft = yOut;

    #pragma simd
    for (int xOut = 0; xOut < nWidth; xOut++)
    {
Compiler output: 
Convolve.cpp(24): (col. 17) remark: LOOP WAS VECTORIZED

Program output:
120 Cores: 0.0087 ms
Compiler output:
Convolve.cpp(24): (col. 9) remark: **SIMD** LOOP WAS VECTORIZED

Program output:
120 Cores: 0.00305 
            image   filter  exec Time (ms)
OpenMP  2048x2048   3x3     4.3
OpenCL  2048x2048   3x3     1.04

Speedup: 4.1X