在opnecl中使用2D卷积进行基准测试的Xeon Phi的性能似乎比使用编译器启用的矢量化的openmp实现要好得多。 Openmp版本以phi native模式运行,并且定时仅测量计算部分:For-loop。对于opencl实现,时序也仅用于内核计算:不包括数据传输。 OpenMp-enbaled版本使用2,4,60,120,240个线程进行了测试。 - 240个线程为平衡线程亲和性设置提供了最佳性能。但即使对于240线程的openmp基线,Opencl也要好17倍,而pragma-enbled矢量化是源代码。输入图像尺寸为1024x1024至16384x16384,滤镜尺寸为3x3至17x17。在调用运行中,opencl比openmp更好。这是opencl的预期加速吗?看起来好得令人难以置信。
编辑:
编译(openmp)
icc Convolve.cpp -fopenmp -mmic -O3 -vec-report1 -o conv.mic
Convolve.cpp(71): (col. 17) remark: LOOP WAS VECTORIZED
来源(Convole.cpp):
void Convolution_Threaded(float * pInput, float * pFilter, float * pOutput,
const int nInWidth, const int nWidth, const int nHeight,
const int nFilterWidth, const int nNumThreads)
{
#pragma omp parallel for num_threads(nNumThreads)
for (int yOut = 0; yOut < nHeight; yOut++)
{
const int yInTopLeft = yOut;
for (int xOut = 0; xOut < nWidth; xOut++)
{
const int xInTopLeft = xOut;
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;
#pragma ivdep //discards any data dependencies assumed by compiler
#pragma vector aligned //all data accessed in the loop is properly aligned
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;
}
}
}
来源2(convolve.cl)
__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;
}
OpenMP的结果(与OpenCL相比):
image filter exec Time (ms)
OpenMP 2048x2048 3x3 23.4
OpenCL 2048x2048 3x3 1.04*
*原始内核执行时间。不包括PCI总线上的数据传输时间。
答案 0 :(得分:2)
之前:(内部最内圈的#pragma ivdep
和#pragma vector aligned
):
Compiler output:
Convolve.cpp(24): (col. 17) remark: LOOP WAS VECTORIZED
Program output:
120 Cores: 0.0087 ms
在@jprice的建议之后(在横向数据上使用#pragma simd):
Compiler output:
Convolve.cpp(24): (col. 9) remark: **SIMD** LOOP WAS VECTORIZED
Program output:
120 Cores: 0.00305
OpenMP现在2.8X
与之前的执行速度相比更快。现在可以使用OpenCL进行公平的比较!
感谢jprice和所有贡献的人。从大家那里学到了很多教训。
编辑: 以下是我的结果和比较:
image filter exec Time (ms)
OpenMP 2048x2048 3x3 4.3
OpenCL 2048x2048 3x3 1.04
Speedup: 4.1X
确实OpenCL可以比OpenMP更快吗?
答案 1 :(得分:1)
英特尔的OpenCL实施将使用他们所称的&#34;隐式矢量化&#34;为了利用矢量浮点单位。这涉及将工作项映射到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++)
{
当我使用ICC编译它时,它会报告它已成功地向所需的循环进行矢量化。
答案 2 :(得分:1)
您的OpenMP程序对一行图像使用一个线程。同一行中的像素是矢量化的。它等于你在OpenCL中有一个维度的工作组。每个工作组处理一行图像。但是在你的OpenCL代码中,似乎你有一个二维工作组。每个工作组(映射到phi上的一个线程)正在处理图像的BLOCK,而不是图像的ROW。缓存命中将有所不同。