2

我正在尝试优化我的应用程序关键部分的性能。用 C 语言编写的代码循环遍历 sourceImage 的所有像素,并计算到每个邻居的“颜色距离”,决定是否记录从 colorDistance 派生的值,然后再转到下一个邻居。

在 XCode 中检测应用程序显示,70% 的时间花在看似简单的浮点计算上——比具有三个 powf 和一个 sqrtf 的代码行长七倍(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])
4

4 回答 4

2

将其1.0转换为 a1.0f并确保MAX_COLOR_DISTANCE定义为<something>.0f,否则在您极其昂贵的线路上会有很多隐式类型转换。

你正在做的那种划分并不是特别昂贵。在 ARM 上,昂贵的是整数除法,因为——不管你信不信——在 ARMv7s 指令集之前没有内置的整数除法。浮点除法要快得多,至少如果你坚持单精度的话。

有没有你没有提到的额外限制?我注意到您的颜色距离公式与人类视觉感知颜色的方式并不真正相关。

在 iOS 上,至少从 5 开始,它也是一种将其踢出到 GPU 的选项,因为您可以直接访问纹理缓冲区,从而消除在 OpenGL 之间来回传递数据的成本。这是一个选择吗?

于 2013-04-04T21:47:17.463 回答
2
           const MAX_COLOR_DISTANCE_RSP = 1 / MAX_COLOR_DISTANCE;
           float attackForce = 1.0 - (colorDistance * MAX_COLOR_DISTANCE_RSP);

此外:用于高速 sqrt 和 recip 估计的Neon Intrinsics比可以根据需要更准确。这将替换您的距离 sqrt。最后,不要使用powf, use 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 位读取快得多。

此外,所有这些全局访问可能会损害您的缓存。尝试将它们全部放在一个结构中以确保它们是相邻的。它还将帮助编译器进行本地池管理。

于 2013-04-04T17:41:11.327 回答
1

如果周期确实用于计算attackForce,您可以预先计算一个将值映射colorDistanceattackForce值的表,并用量化操作和查找替换您的除法。

于 2013-04-04T17:03:07.980 回答
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)它仍然会更快。当你的参数是整数时为什么要使用浮点数?

于 2013-04-04T18:03:53.923 回答