我试图优化此功能:
bool interpolate(const Mat &im, float ofsx, float ofsy, float a11, float a12, float a21, float a22, Mat &res)
{
bool ret = false;
// input size (-1 for the safe bilinear interpolation)
const int width = im.cols-1;
const int height = im.rows-1;
// output size
const int halfWidth = res.cols >> 1;
const int halfHeight = res.rows >> 1;
float *out = res.ptr<float>(0);
const float *imptr = im.ptr<float>(0);
for (int j=-halfHeight; j<=halfHeight; ++j)
{
const float rx = ofsx + j * a12;
const float ry = ofsy + j * a22;
#pragma omp simd
for(int i=-halfWidth; i<=halfWidth; ++i, out++)
{
float wx = rx + i * a11;
float wy = ry + i * a21;
const int x = (int) floor(wx);
const int y = (int) floor(wy);
if (x >= 0 && y >= 0 && x < width && y < height)
{
// compute weights
wx -= x; wy -= y;
int rowOffset = y*im.cols;
int rowOffset1 = (y+1)*im.cols;
// bilinear interpolation
*out =
(1.0f - wy) *
((1.0f - wx) *
imptr[rowOffset+x] +
wx *
imptr[rowOffset+x+1]) +
( wy) *
((1.0f - wx) *
imptr[rowOffset1+x] +
wx *
imptr[rowOffset1+x+1]);
} else {
*out = 0;
ret = true; // touching boundary of the input
}
}
}
return ret;
}
我使用英特尔顾问进行优化,即使内部for
已经过矢量化,英特尔顾问也检测到了低效的内存访问模式:
特别是在以下三个指令中有4个聚集(不规则)访问:
当被访问的元素属于a[b]
类型,其中b
是不可预测的时,我会理解收集访问的问题。这似乎是imptr[rowOffset+x]
的情况,其中rowOffset
和x
都是不可预测的。
与此同时,我看到Vertical Invariant
当使用常量偏移量访问元素时,应该发生这种情况(再次,根据我的理解)。但实际上我不知道这个常数偏移的位置
所以我有3个问题:
使用以下标志与icpc
2017更新3进行编译:
INTEL_OPT=-O3 -ipo -simd -xCORE-AVX2 -parallel -qopenmp -fargument-noalias -ansi-alias -no-prec-div -fp-model fast=2 -fma -align -finline-functions
INTEL_PROFILE=-g -qopt-report=5 -Bdynamic -shared-intel -debug inline-debug-info -qopenmp-link dynamic -parallel-source-info=2 -ldl
答案 0 :(得分:1)
向量化(SIMD调整)代码不会自动使您的访问模式更好(或更糟)。 为了最大化矢量化代码的性能,您必须尝试在代码中使用 unit stride (也称为连续,线性,stride-1)内存访问模式。或至少“ 可预测”的常规跨步N,理想情况下,N应该是适度的低值。
不引入这种规律性-您可以在指令级将内存的LOAD / STORE操作保持部分顺序(非并行)。因此,每次您要进行“并行”加法/乘法等操作时,都必须进行“非并行”原始数据元素“收集”。
在您的情况下,在逻辑上似乎存在规则的跨度N-从代码片段和Advisor MAP输出(在右侧面板上)都可以看到。 垂直不变-表示您有时在两次迭代之间访问相同的内存位置。 步幅表示在其他情况下您具有逻辑上连续的内存访问权限。
但是,代码结构很复杂:循环主体中有if语句,条件复杂,并且有浮点->整数(简单但仍然)转换。
因此,编译器必须“以防万一”使用大多数通用且效率最低的方法(收集),结果您的物理,事实内存访问模式(来自编译器代码生成)是不规则的“ GATHER”,但是从逻辑上讲,您的访问模式是规则的(不变或单位步幅)。
解决方案可能不是很容易,但是我会尝试以下操作: