Можно ли оптимизировать этот код? - PullRequest
7 голосов
/ 31 марта 2009

У меня есть некоторый код обработки изображений, который проходит через 2 многомерных массива байтов (того же размера). Он берет значение из исходного массива, выполняет для него вычисления, а затем сохраняет результат в другом массиве.

int xSize = ResultImageData.GetLength(0);
int ySize = ResultImageData.GetLength(1);

for (int x = 0; x < xSize; x++)
{                
   for (int y = 0; y < ySize; y++) 
   {                                                
      ResultImageData[x, y] = (byte)((CurrentImageData[x, y] * AlphaValue) +
                                    (AlphaImageData[x, y] * OneMinusAlphaValue));
   }
}

Цикл в настоящее время занимает ~ 11 мс, что, как я полагаю, связано в основном с доступом к значениям байтовых массивов, так как вычисления довольно просты (2 умножения и 1 сложение).

Что я могу сделать, чтобы ускорить это? Это критичная ко времени часть моей программы, и этот код вызывается 80-100 раз в секунду, поэтому любое увеличение скорости, даже небольшое, будет иметь значение. Также в данный момент xSize = 768 и ySize = 576, но в будущем это увеличится.

Обновление : Благодаря Гуффа (см. Ответ ниже), следующий код экономит мне 4-5 мс на цикл. Хотя это небезопасный код.

int size = ResultImageData.Length;
int counter = 0;
unsafe
{
    fixed (byte* r = ResultImageData, c = CurrentImageData, a = AlphaImageData)
    {
        while (size > 0)
        {
            *(r + counter) = (byte)(*(c + counter) * AlphaValue + 
                                    *(a + counter) * OneMinusAlphaValue);
            counter++;
            size--;
        }
    }
}

Ответы [ 14 ]

1 голос
/ 31 марта 2009

442 368 сложений и 884 736 умножений для расчета, я бы подумал, что 11 мс на современном процессоре чрезвычайно медленные.

хотя я не знаю много о специфике .net, я знаю, что высокоскоростной расчет не является сильной стороной. В прошлом я создавал Java-приложения с похожими проблемами, я всегда использовал библиотеки C для обработки изображений / аудио.

исходя из аппаратной точки зрения, вы хотите убедиться, что доступ к памяти является последовательным, то есть пошагово проходит через буфер в том порядке, в котором он существует в памяти. вам также может понадобиться изменить порядок так, чтобы компилятор использовал доступные инструкции, такие как SIMD. Как это сделать, в конечном итоге будет зависеть от вашего компилятора, и я не могу помочь с vs.net.

на встроенном DSP я бы вырвался

(AlphaImageData [x, y] * OneMinusAlphaValue) и (CurrentImageData [x, y] * AlphaValue) и использовать инструкции SIMD для вычисления буферов, возможно, параллельно перед выполнением сложения. возможно, делать достаточно маленькие куски, чтобы сохранить буферы в кэше процессора.

Я считаю, что все, что вы делаете, потребует более прямого доступа к памяти / процессору, чем позволяет .net.

1 голос
/ 31 марта 2009

Если CurrentImageData и / или AlphaImageData не меняются при каждом запуске фрагмента кода, вы можете сохранить продукт до запуска фрагмента кода, который вы показываете, и избежать такого умножения в циклах.

Редактировать: Еще одна вещь, о которой я только что подумал: иногда операции int выполняются быстрее, чем операции байтов. Сместите это с использованием кеша вашего процессора (вы значительно увеличите размер данных и увеличите риск потери кеша).

1 голос
/ 31 марта 2009

Должны ли данные изображения храниться в многомерном (прямоугольном) массиве? Если вместо этого вы используете неровные массивы, вы можете обнаружить, что в JIT доступно больше оптимизаций (включая удаление проверки границ).

1 голос
/ 31 марта 2009

Если вы используете LockBits для доступа к буферу изображения, вы должны пройти через y во внешнем цикле и x во внутреннем цикле, так как он хранится в памяти (по строке, а не по столбцу). Я бы сказал, что 11 мс - чертовски быстро, хотя ...

...