我正在尝试优化应用关键部分的效果。用C编写,代码循环遍历sourceImage的所有像素,并计算每个邻居的“颜色距离”,决定是否记录从colorDistance派生的值,然后再转移到下一个邻居。
在XCode中检测应用程序显示,70%的时间用于看似简单的浮点计算 - 比具有三个powf和一个sqrtf的代码行长7倍(colorDistance的计算消耗10.8%) 。
在下面某些代码行的左侧,您将看到从XCode Instruments复制时所花费的时间百分比。 (我也注意到其他平凡的代码行令人惊讶地具有相对较高的百分比,即使与我上面提到的不相近)。
非常感谢任何关于优化位置和方式的提示。
欢呼声
for (int row = 1; row < height - 1; row++)
{
for (int col = 1; col < width - 1; col++)
{
int pixelIndex = (col + row * width);
1.7% int pixelIndexIntoImage = pixelIndex * COMPONENTS_PER_PIXEL;
// loop over pixel's 8 neighbours clockwise starting from neighbor id 0
// using Nx[] and Ny[] as guides to calculate neighbour locations
1.6% for (int n = 0; n < 8; n++)
{
5.3% int neighborIndex = pixelIndex + Nx[n] + width * Ny[n];
int neighborIndexIntoImage = neighborIndex * COMPONENTS_PER_PIXEL;
// skip neighbors that are not a foreground or background
3.3% uint8_t labelValue = labelsMap[neighborIndex];
1.1% if (labelValue == LABEL_UNKNOWN_VALUE)
continue;
// "color distance" between the pixel and the current neighbour
float colorDistance;
1.4% if(numColorComponents == 3)
{
5.3% uint8_t redPixel = sourceImage[pixelIndexIntoImage ];
uint8_t grnPixel = sourceImage[pixelIndexIntoImage+1];
uint8_t bluPixel = sourceImage[pixelIndexIntoImage+2];
uint8_t redNeigh = sourceImage[neighborIndexIntoImage ];
uint8_t grnNeigh = sourceImage[neighborIndexIntoImage+1];
uint8_t bluNeigh = sourceImage[neighborIndexIntoImage+2];
10.8% colorDistance = sqrtf( powf(redPixel-redNeigh, 2) +
powf(grnPixel-grnNeigh, 2) +
powf(bluPixel-bluNeigh, 2));
}
else
{
uint8_t pixel = sourceImage[pixelIndexIntoImage ];
uint8_t neigh = sourceImage[neighborIndexIntoImage];
colorDistance = fabsf(pixel - neigh);
}
71.2% float attackForce = 1.0 - (colorDistance / MAX_COLOR_DISTANCE);
if (attackForce * strengthMap[neighborIndex] > revisedStrengthMap[pixelIndex])
{
//attack succeeds
strengthMap[pixelIndex] = attackForce * revisedStrengthMap[neighborIndex];
outputMask[pixelIndex] = labelsMap[neighborIndex];
isConverged = false; // keep iterating
}
}
}
}
变量的定义
uint8_t *sourceImage; // 4 bytes per pixel
uint8_t *labelsMap, *outputMask; // 1 byte per pixel
int numPixels = width * height;
float *strengthMap = (float*) malloc(sizeof(float)*numPixels);
float *revisedStrengthMap = (float*) malloc(sizeof(float)*numPixels);
short Nx[] = {-1, 0, 1, 1, 1, 0, -1, -1};
short Ny[] = {-1, -1, -1, 0, 1, 1, 1, 0};
根据我收到的建议(乘法比分部“便宜”),我修改了一个代码行,有趣的是,71.2%下降到1.7%,但是“if”声明刚好低于64.8% - 我只是不明白!
1.7% float attackForce = 1.0 - (colorDistance * MAX_COLOR_DISTANCE_INV);
64.8% if (attackForce * strengthMap[neighborIndex] > revisedStrengthMap[pixelIndex])
答案 0 :(得分:2)
const MAX_COLOR_DISTANCE_RSP = 1 / MAX_COLOR_DISTANCE;
float attackForce = 1.0 - (colorDistance * MAX_COLOR_DISTANCE_RSP);
另外:Neon Intrinsics用于高速sqrt和recip估计,而不是根据需要更准确。这将取代您的距离sqrt。最后,不要使用powf
,使用val * val
,因为编译器可能不会将该函数优化为一个简单的mul。
您还可以通过单次读取读取整个像素(假设32位对齐,应该是RGBA文件格式的情况):
uint32_t *sourceImage = (uint32_t *)(&sourceImage[pixelIndexIntoImage]);
uint8_t pixels[4];
*(uint32_t *)(&pixels[0]) = *sourceImage;
现在你的像素阵列已准备好读取所有4个组件,但你必须仔细研究,找出哪个像素由于字节序问题而具有哪种颜色。一次32位读取比3位,8位读取快得多。
此外,所有这些全局访问可能会损害您的缓存。尝试将它们全部放在一个结构中以确保它们相邻。它还将帮助编译器进行本地池管理。
答案 1 :(得分:2)
将1.0
转换为1.0f
并确保MAX_COLOR_DISTANCE
定义为<something>.0f
,否则您的极其昂贵的行上会有大量隐式类型转化。
你所做的那种分工并不是特别贵;在ARM上,什么是昂贵的整数除法,因为 - 信不信由你 - 在ARMv7s指令集之前没有内置的整数除法。浮点除法要快得多,至少如果你坚持单精度。
你没有提及任何额外的限制吗?我注意到你的色彩距离公式并没有真正与人类视觉感知色彩的方式相关联。
在iOS上,至少从5开始,它也可以选择将其发送到GPU,因为您可以直接访问纹理缓冲区,从而消除了在OpenGL之间来回传递数据的成本。这是一个选择吗?
答案 2 :(得分:1)
如果在计算attackForce
时确实花费了周期,则可以预先计算将colorDistance
值映射到attackForce
值的表,并使用量化操作和查找替换您的除法
答案 3 :(得分:0)
乘法:
int pixelIndex = (col + row * width);
int pixelIndexIntoImage = pixelIndex * COMPONENTS_PER_PIXEL;
可以更改为附加内容。这几乎适用于使用指数的任何地方。
方法调用:
colorDistance = sqrtf( powf(redPixel-redNeigh, 2) +
powf(grnPixel-grnNeigh, 2) +
powf(bluPixel-bluNeigh, 2));
请勿在此处使用powf
。您只需使用(grnPixel-grnNeigh)*(grnPixel-grnNeigh)
它就会更快。为什么在你的参数是integeres时使用浮点数?