[英]performance optimizing critical code in iOS app
我正在嘗試優化應用程序關鍵部分的性能。 該代碼以C語言編寫,循環遍歷sourceImage的所有像素,並計算到其每個鄰居的“顏色距離”,從而決定在繼續到下一個鄰居之前是否記錄從colorDistance派生的值。
在XCode中對應用程序進行檢測后發現,有70%的時間花在了看似簡單的float計算上,比具有三個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])
const MAX_COLOR_DISTANCE_RSP = 1 / MAX_COLOR_DISTANCE;
float attackForce = 1.0 - (colorDistance * MAX_COLOR_DISTANCE_RSP);
另外: Neon Intrinsics用於高速sqrt和配方估計,可以根據需要逐步提高精度。 這將取代您的距離sqrt。 最后,不要使用powf
,請使用val * val
因為編譯器可能不會為您優化該功能。
您還可以通過一次讀取來讀取整個像素(假設32位對齊方式應為RGBA文件格式):
uint32_t *sourceImage = (uint32_t *)(&sourceImage[pixelIndexIntoImage]);
uint8_t pixels[4];
*(uint32_t *)(&pixels[0]) = *sourceImage;
現在,您的像素陣列已准備就緒,可以讀取所有4個成分,盡管您必須稍作試驗才能找出由於像素問題而導致哪個像素具有哪種顏色。 一個32位讀取比3個8位讀取快得多。
同樣,所有這些全局訪問可能會損害您的緩存。 嘗試將它們全部放置在單個結構中,以確保它們相鄰。 它還將幫助編譯器進行本地池管理。
將該1.0
轉換為1.0f
,並確保將MAX_COLOR_DISTANCE
定義為<something>.0f
,否則,在非常昂貴的行上會有很多隱式類型轉換。
您要進行的划分並不是特別昂貴; 在ARM上,昂貴的是整數除法,因為-信不信由你-在ARMv7s指令集之前沒有內置的整數除法。 浮點除法要快得多,至少要堅持單精度。
您還有其他約束要提及嗎? 我注意到您的色距公式與人的視覺感知顏色並不真正相關。
在iOS上(至少從5開始),也可以將其踢出GPU,因為您可以直接訪問紋理緩沖區,而無需在OpenGL之間來回傳遞數據。 那是一個選擇嗎?
如果真的周期正在在計算花費attackForce
,你可以預先計算表映射colorDistance
值attackForce
值,並用量化操作和查找替換您的分工。
乘法:
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)
它將仍然更快。 當參數為整數時,為什么要使用浮點數?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.