Быстрая адресация массива

Я запускаю код анализа изображения в массиве, хранящем информацию об изображении. К сожалению, код очень тяжелый и занимает в среднем 25 секунд для запуска через один кадр. Основная проблема, которую я вижу, - это адресация массива. Какой из них наиболее быстрый для запуска через 2d-массив и существуют ли какие-либо различия в

горизонтальный, а затем вертикальный

for (int y = 0; y < array.Length; ++y)
    for (int x = 0; x < array[].Length; ++x)
        //Code using array[y][x]

и вертикальный, затем горизонтальный?

for (int x = 0; x < array[].Length; ++x)
    for (int y = 0; y < array.Length; ++y)
        //Code using array[y][x]

Кроме того, я попытался избежать прямой адресации и использовать указатели вместо этого.

for (int y = 0; y < array.Length; ++y)
    int* ptrArray = (int*)array[0];
    for (int x = 0; x < array[].Length; ++x, ++ptrArray)
        //Code using ptrArray for array[y][x]

или

for (int x = 0; x < array[].Length; ++x)
    int* ptrArray = (int*)array[0];
    for (int y = 0; y < array.Length; ++y, ptrArray += array[].Length)
        //Code using ptrArray for array[y][x]

Любая помощь приветствуется. Макс

Ответ 1

Один из вариантов - использовать обратный цикл (запустите for() loop от array.Length до 0)

Это ускорит работу.

например,

for (int x = array[].Length-1; x >= 0; --x)
    int* ptrArray = (int*)array[0];
    for (int y = array.Length-1; y >= 0 ; --y, ptrArray += array[].Length)
        //Code using ptrArray for array[y][x]

Ответ 2

Самое главное правило заключается в том, что вся эта теория до тех пор, пока вы не профилируете. Я не согласен с теми, кто настаивает на том, что профилирование - это все (без какой-либо теории вы не лучше, чем Cargo Cultist, кладя кокосовые орехи в уши и ожидая прибытия самолета), но ваша теория всегда может быть неправильной или неполной, поэтому профилирование имеет решающее значение.

Как правило, мы хотим, чтобы внутреннее сканирование было горизонтально (с точки зрения массива, а не изображения, хотя для большинства форматов это то же самое). Причина в том, что с массивом вроде:

00 01 02 03 04 05 06 07 08 09
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29

Он будет выложен как:

00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

Вы хотите сканировать по смежным блокам, которые могут быть загружены в кэши ЦП, а затем использованы целиком, а не сканировать с блока на блок и нуждаться в регулярном изменении содержимого кэша ЦП.

Это еще более важно, если вы попытаетесь параллелизовать алгоритм. Вы хотите, чтобы каждый поток работал с его собственными смежными блоками памяти, насколько это касается как входа, так и выхода, а не не только страдает от того, как однопоточный код работает с плохим чередованием частоты, но также вызывает загрязнение других буферов и необходимо освежить. Это может быть разница между параллелизмом, ведущим к ускорению скорости и параллелизующим, фактически замедляющим работу.

Другое дело - разница между 2-мерным массивом byte[,], а не массивом массивов byte[][], который ваш комментарий в вашем вопросе "array [y] [x]" заставляет меня задаться вопросом, может быть, вы используете, С первым, чтобы получить arr [1,2], логика:

  • Проверить границы
  • Вычислить позицию (простая быстрая арифметика)
  • Получить значение.

С последним логика такова:

  • Проверить границы
  • Получить массив через указатель.
  • Проверить границы
  • Получить значение.

Существует также менее хорошая частота кэширования памяти. Последнее имеет преимущества, когда нужны "зубчатые" структуры, но здесь это не так. 2D почти всегда быстрее массива массивов.

То, что я не вижу, скорее всего, поможет, но я бы, конечно, попробовал их в вашей ситуации:

Вы можете найти повышение от выполнения вашей логики 1d <= > 2d. Имейте одномерный массив, где idx = y * width + x. Это не должно иметь заметной разницы, но стоит попробовать.

Оптимизации стараются как вызвать вызовы на .Length, так и опустить ненужную проверку границ, поэтому вы можете найти ручную подъемку и переключиться на арифметику указателя, ничего не получится, но в случае, когда вам действительно нужно сбить время это, безусловно, стоит профилировать.

Наконец-то. Профилировали ли вы, как быстро ваш код просматривает массив и ничего не делает? Возможно, что другая часть кода является настоящим узким местом, и вы исправляете неправильную вещь.

Ответ 3

Я понятия не имею, но вы уже придумали примеры. Таким образом, вы можете запускать свои образцы кода в цикле и сами профиль.

var sw = new Stopwatch();
sw.Start();
ExecuteMyCode();
sw.Stop();
Console.WriteLine("Time: " + sw.Elapsed);

Возможно, вы сможете ускорить обработку, используя многопоточную конструкцию как Parallel.ForEach. Это будет хорошо работать, если код в вашем цикле избегает зависимостей между итерациями цикла.

Ответ 4

Можете ли вы гой небезопасно? Указатель. Проблема с массивом заключается в том, что вы STILL имеют граничные проверки при каждом доступе. Указатели удаляют это. Обратите внимание, что это полностью поддерживается С#, но вам нужно поместить его в небезопасный блок. Это также означает, что вы должны быть ABLE для запуска небезопасного кода, который не всегда является данным.

http://msdn.microsoft.com/en-us/library/28k1s2k6.aspx

имеет образец кода.

Ответ 5

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

Ответ 6

Всегда следите за тем, чтобы ваш внутренний контур обращался к непрерывной памяти.

Обычно это строка вашего изображения. Обратите внимание, что в прямоугольных массивах вы должны сделать это последним индексом: array[y,x].

этот документ предполагает, что встроенные прямоугольные массивы С# (с несколькими индексами) довольно медленные. Я читал это раньше, но это единственная ссылка, которую я получил. Я бы начал с линейного массива и вычислил смещение один раз для каждой строки. Неуправляемый поможет вам в действительно тривиальных случаях.

Если один кадр занимает 25 секунд, то он либо huuuuge, либо очень сложная обработка. В этом случае интересно только потратить усилия на оптимизацию доступа к памяти, если вы получаете доступ ко многим входным пикселям для каждого выходного пикселя.