以下代码用于卷积图像。所述图像的每个像素由以下表示:
typedef struct {
unsigned short red; /* R value */
unsigned short green; /* G value */
unsigned short blue; /* B value */
} pixel;
可以看出,RGB值具有16位表示(“16位颜色”)。图像I被存储为一维像素阵列,其中第(i,j)像素是I [RIDX(i,j,n)]。这里n是图像矩阵的维数,和 RIDX是一个定义如下的宏
#define RIDX(i,j,n) ((i)*(n)+(j))
对于大多数目的,你可以认为I [RIDX(i,j,n)]等同于I [i] [j]。最后,我需要使用代码运动,循环展开和阻塞等技术优化下面的代码。
char naive_convolve_descr[] = "naive_convolve: Naive baseline implementation";
void naive_convolve(int dim, pixel *src, pixel *dst)
{
int i, j, ii, jj, curI, curJ;
pixel_sum ps;
for (j = 0; j < dim; j++){
for (i = 0; i < dim; i++){
ps.red = 0.0;
ps.green = 0.0;
ps.blue = 0.0;
ps.weight = 0.0;
for (jj = -2; jj <= 2; jj++){
for (ii = -2; ii <= 2; ii++){
curJ = j+jj;
if(curJ<0 || curJ>=dim){
continue;
}
curI = i+ii;
if(curI<0 || curI>=dim){
continue;
}
ps.red += src[RIDX(curI, curJ, dim)].red * kernel[ii+2][jj+2];
ps.green += src[RIDX(curI, curJ, dim)].green * kernel[ii+2][jj+2];
ps.blue += src[RIDX(curI, curJ, dim)].blue * kernel[ii+2][jj+2];
ps.weight += kernel[ii+2][jj+2];
}
}
dst[RIDX(i,j,dim)].red = (unsigned short)(ps.red/ps.weight);
dst[RIDX(i,j,dim)].green = (unsigned short)(ps.green/ps.weight);
dst[RIDX(i,j,dim)].blue = (unsigned short)(ps.blue/ps.weight);
}
}
}
我的内核是
//emboss top-right kernel
Kernel emboss_tr_kernel =
{
{0.0, -1.0, -1.0, -1.0, -1.0},
{1.0, 0.0, -4.0, -16.0, -1.0},
{1.0, 4.0, 1.0, -4.0, -1.0},
{1.0, 16.0, 4.0, 0.0, -1.0},
{1.0, 1.0, 1.0, 1.0, 0.0}
};
答案 0 :(得分:0)
评论太大了,所以我发帖作为答案。
实际上,通过更改两个外循环的顺序,您可能获得最大的加速。你的内循环遍历i
。但src[RIDX(curI, curJ, dim)]
扩展为((curI)*(dim)+(curJ))
。因此,每2个连续curI
s在内存中彼此相距dim
个像素。因此,您将以自己的方式生成大量缓存未命中 - 迭代列。
相反,迭代行。
接下来考虑将仅与jj
循环相关的代码段移动到它的循环而不是最里面的循环。如:
for (jj = -2; jj <= 2; jj++){
curJ = j+jj;
if(curJ<0 || curJ>=dim){
continue;
}
相反,您也可以使用
for(curJ=max(0, j-2); curJ <= min(dim-1, j+2); ++curJ)
接下来看看你是否可以分解你的内核线性可分离内核。如果是这样,请重写代码以利用它。 (编辑:我不认为这是可能的,因为你的矩阵有完整的排名。)
然后最后考虑是否可以并行化代码。但是请注意不要以导致多个线程共享同一缓存行的方式执行此操作。 http://www.drdobbs.com/parallel/eliminate-false-sharing/217500206
答案 1 :(得分:0)
有几种优化机会。
选择选项2后,在单个平面中对数据进行解交织,例如
rows -2, -1: all zeroes
rows 0..dim-1: [0 0][green][0 0 0 0][red][0 0 0 0][blue][0 0]
rows dim, dim+1: all zeroes
力量减少&#34;总和+ =体重&#34;是不变的(错误是1吗?) - 无需每次都计算
强度将内循环中的src指针计算减少为(src + = dim_padded;)
使用内在函数
进行手动SIMD矢量化1)使用常数+ - {1,4,16}作为整数
写出表达式2) 内核有两个维度的运行,与附近的单元共享总和:
[0 h h h h]
[ ]
[ ]
[ ]
[0[h h h h] ] <-- here the sum `h+h+h+h` is shared between the two
[V - d - v] positions.
[V D - d v]
[V - D - v]
[H H H H -]
此处Sum(v)[row][column_X] == -Sum(v)[row][column_X + 4]
和Sum(H)[row][column] == -Sum(h)[row + 4][column - 1]
因此,通过将3和4个元素的垂直和水平和计算为临时数组,可以简化计算(和存储器带宽)。
然后内循环只需要访问11个项目。类似的情况可以用于对角线总和d = -D
,这可能会降低投资收益。