Счетчик петель и указатели - PullRequest
3 голосов
/ 14 июня 2011

Я пишу небольшую специализированную библиотеку C99 для графиков, и я часто получаю циклы вида:

for(int i = 0; i < graph->nvertices; ++i) {
  // ...
}

Мне интересно, хорошая ли это практика, особенно в случае узкой петли.Сначала я подумал, что компилятор будет достаточно умен, чтобы смотреть на 'graph-> nvertices' только один раз, вместо того, чтобы рассматривать его на каждой итерации, но это кажется невозможным, поскольку graph-> nvertices может меняться внутри цикла.Это умнее и быстрее писать:

const int N = graph->nvertices;
for(int i = 0; i < N; ++i) {
  // ...
}

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

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

Ответы [ 7 ]

3 голосов
/ 14 июня 2011

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

for (int i = graph->nvertices; i >= 0; --i) 
  ..

Однако вы собираетесь нанести ущерб производительности кэша.Я думаю, что метод, который вы предложили, является наиболее простым, который понятен компилятору и следующему человеку, читающему ваш код.

3 голосов
/ 14 июня 2011

Я склонен делать эти оптимизации самостоятельно.Иногда компилятор может сделать вывод, что nvertices не изменяется по всему циклу, но что если у вас есть вызов других функций, которые могут изменить значение?Компилятор не может вывести его и не может оптимизировать код.

Кроме того, лучший способ всегда - профилировать код, чтобы увидеть сравнение между двумя подходами.

1 голос
/ 14 июня 2011

Вы попросили посмотреть на ассемблерный код. Для этого вы можете использовать программу objdump, например:

objdump -d executable

Чтобы отфильтровать основную функцию, используйте:

objdump -d executable | sed -n '/<main>/,/^$/p'
1 голос
/ 14 июня 2011

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

Однако, если бы меня действительно волновал каждый бит производительности, я мог бы создать отдельную переменную count, как вы предлагаете.

В конце концов, я сомневаюсь, что это внесет заметную разницу.

1 голос
/ 14 июня 2011

Я использую это обычно.

const int N = graph->nvertices;
int i = 0;
for(; i < N; ++i) {
  // ...
}
0 голосов
/ 14 июня 2011

Я бы пошел для локализации области видимости переменной. Затем оптимизатор сам определится:

for(size_t i = 0, n = graph->nvertices; i < n; ++i) {
  // ...
}
0 голосов
/ 14 июня 2011

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

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

...