Правильно ли я понимаю что все современные компиляторы без проблем оптимизируют код типа:
for(int i = 0; i < s.size; ++i) {
и писать типа
int size = s.size();
for(int i = 0; i < size; ++i) {
уже не нужно? или в зависимости от того чем является s (переменная в примерах выше) — могут быть какие-то проблемы?
Ну и понятное дело, я говорю о ситуации когда длина не меняется в теле for
А есть ли отличия для while?
Звучит вопрос глупо, но вдруг есть какие-то интересные, но не очевидные для меня нюансы.
Re: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, sanx, Вы писали:
S>Звучит вопрос глупо, но вдруг есть какие-то интересные, но не очевидные для меня нюансы.
Нет, вы неправильно думаете. Это общее заблуждение относительно того что по умолчанию могут, а чего нет современные компиляторы.
Берём сферический случай в вакууме.
void test(void)
{
int i;
int size = get_size();
for(i = 0; i < size; ++i) {
call_smth();
}
}
void test2(void)
{
int i;
for(i = 0; i < get_size(); ++i) {
call_smth();
}
}
Компилируем gcc-4.4.3 на уровне O2 до стадии GIMPLE, смотрим дамп
;; Function test (test)
test ()
{
int size;
int i;
<bb 2>:
size = get_size ();
if (size > 0)
goto <bb 3>;
else
goto <bb 5>;
<bb 3>:
i = 0;
<bb 4>:
call_smth ();
i = i + 1;
if (size > i)
goto <bb 4>;
else
goto <bb 5>;
<bb 5>:
return;
}
Обратите внимание -- вычисление происходит один раз.
;; Function test2 (test2)
test2 ()
{
int i;
int D.1607;
<bb 2>:
i = 0;
goto <bb 4>;
<bb 3>:
call_smth ();
i = i + 1;
<bb 4>:
D.1607 = get_size ();
if (i < D.1607)
goto <bb 3>;
else
goto <bb 5>;
<bb 5>:
return;
}
Обратите внимание -- колбасим каждый цикл.
Сейчас я вам коротко объясню почему это так происходит и, я так полагаю, всегда будет время от времени при определённых условиях происходить на любом оптимизирующем компиляторе. В общем случае -- мы не знаем что делает функция get_size. Она где-то в другой единице линковки и мы не представляем что этой единице линковки происходит. Чёрт, она может быть вообще скомпилирована отдельно! Да, компилятор может иногда смухлевать. Есть межпроцедурные оптимизации. Есть инлайнинг. Например в gcc опция combine вообще заставляет трактовать все входные файлы как один. Но в самом общем случае, компилятор *не понимает*, что семантически get_size вычисляет и возращает каждый раз один и тот же размер. Он не может это доказать и поэтому ведёт себя максимально консервативно, а именно -- даже на высоких уровнях оптимизации делает ровно то, что вы ему сказали.
Именно поэтому когда вы пишете код, который потом будет обрабатываться компилятором -- будьте пожалуйста предельно ясны в выражении своих мыслей. Если результат нужен один раз -- вычислите его, пожалуйста, один раз. У компилятора нет встроенных телепатических средств, которыми он угадывает что делает и что не делает ваш код. Вернее есть, но они работают крайне ненадёжно и в частных случаях.
---
With best regards, Konstantin
Re: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, sanx, Вы писали:
S>Правильно ли я понимаю что все современные компиляторы без проблем оптимизируют код типа:
S>for(int i = 0; i < s.size; ++i) {
S>и писать типа
S>int size = s.size(); S>for(int i = 0; i < size; ++i) {
Зачем переменная size за областью for ?
for (size_t i = 0, size = s.size(); i < size; ++i)
...
Точно также и в итераторах
for (Container::iterator it = c.begin(), it_end = c.end(); it != it_end; ++it)
...
Здравствуйте, sanx, Вы писали:
S>Ну и понятное дело, я говорю о ситуации когда длина не меняется в теле for
Это тебе совершенно очевидно, что длина не меняется в теле for. Компилятору это может быть совсем не очевидно, и он только в достаточно простых случаях может определить, что она не меняется.
for(int i = 0; i < s.size; ++i)
f(&s);
Поди тут пойми, меняется s.size или нет, особенно если f описана в другом файле или вообще в библиотеке какой-то.
With best regards
Pavel Dvorkin
Re: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, sanx, Вы писали:
S>Правильно ли я понимаю что все современные компиляторы без проблем оптимизируют код типа:
S>for(int i = 0; i < s.size(); ++i) {
S>и писать типа
S>int size = s.size(); S>for(int i = 0; i < size; ++i) {
S>уже не нужно? или в зависимости от того чем является s (переменная в примерах выше) — могут быть какие-то проблемы?
Во-первых, перебор всех элементов контейнера нужно делать на итераторах, и лучше в стандартном алгоритме. Или через BOOST_FOREACH.
Во-вторых, отсутствие выигрыша достигается не тем, что компилятор автоматически выдёргивает вызов size() из цикла, а тем, что функция size(), как правило, инлайновая и очень простая (доступ к полю объекта, или доступ к двум полям и вычитание указателей).
Re[2]: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, minorlogic, Вы писали:
M>Возможно автор подразумевал шаблонную функцию, доступ к реализации которой у компилятора есть.
Обязан ли компилятор пересчитывать _End — _Begin?
Допустим может, что произойдет если я из другого потока буду менять этот _End и _Begin?
я не волшебник, я только учусь!
Re[2]: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, Tilir, Вы писали:
T>Именно поэтому когда вы пишете код, который потом будет обрабатываться компилятором -- будьте пожалуйста предельно ясны в выражении своих мыслей. Если результат нужен один раз -- вычислите его, пожалуйста, один раз. У компилятора нет встроенных телепатических средств, которыми он угадывает что делает и что не делает ваш код. Вернее есть, но они работают крайне ненадёжно и в частных случаях.
Согласен, с небольшой оговоркой. Если тебе таки надо проитерировать контейнер, то делай это прямо, так, как это принято делать с такими контейнерами.
Тогда есть большие шансы на то, что авторы библиотеки и компилятора предпримут какие-то усилия и всё таки получится. В отличии от ситуации, когда ты делаешь что-то необычное. Но, пир этом всё равно не стоит надеяться на то, что в компиляторе есть блок телепатии
Ну и не надо надеяться на то, что у того, кто будет этот код поддерживать, тоже есть развитые телепатические способности. А то ведь оптимизация может отвалиться не сразу, а попозже...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Tilir, Вы писали:
T>Здравствуйте, sanx, Вы писали:
S>>Звучит вопрос глупо, но вдруг есть какие-то интересные, но не очевидные для меня нюансы.
T>Нет, вы неправильно думаете. Это общее заблуждение относительно того что по умолчанию могут, а чего нет современные компиляторы.
Элементарные вещи типа get_size() в большинстве библиотек инлайнятся. Соответственно, компилятор все раскрутит и вынесет.
Здравствуйте, IROV.., Вы писали:
M>>Возможно автор подразумевал шаблонную функцию, доступ к реализации которой у компилятора есть. IRO>Обязан ли компилятор пересчитывать _End — _Begin?
Если _End или _Begin отмечены как volatile — обязан прочитать новые значения при очередном вызове size() и, соответственно, вернуть актуальный размер. Иначе — имеет право не пересчитывать, по крайней мере до С++11.
Re: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, Tilir, Вы писали:
T>Сейчас я вам коротко объясню почему это так происходит и, я так полагаю, всегда будет время от времени при определённых условиях происходить на любом оптимизирующем компиляторе. В общем случае -- мы не знаем что делает функция get_size. Она где-то в другой единице линковки и мы не представляем что этой единице линковки происходит. Чёрт, она может быть вообще скомпилирована отдельно! Да, компилятор может иногда смухлевать. Есть межпроцедурные оптимизации. Есть инлайнинг. Например в gcc опция combine вообще заставляет трактовать все входные файлы как один. Но в самом общем случае, компилятор *не понимает*, что семантически get_size вычисляет и возращает каждый раз один и тот же размер. Он не может это доказать и поэтому ведёт себя максимально консервативно, а именно -- даже на высоких уровнях оптимизации делает ровно то, что вы ему сказали.
+1.
Более того, даже если у компилятора есть доступ к реализации get_size() и он может ее заинлайнить, у него может не быть доступа к реализации call_smth(), а даже если он есть, то может не быть доступа к реализации любой из маленьких функций, которые из call_smth() зовутся.
А это значит, что размер контейнера внутри этой неизвестной маленькой функции может измениться и кешировать его нельзя.
Здравствуйте, jazzer, Вы писали:
J>Не поможет. J>доступ должен быть ко всему — и к size, и к телу цикла целиком (со всеми вложенными вызовами), иначе кешировать нельзя.
Да , я про такую ситуацию.
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[3]: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, jazzer, Вы писали:
J>Более того, даже если у компилятора есть доступ к реализации get_size() и он может ее заинлайнить, у него может не быть доступа к реализации call_smth(), а даже если он есть, то может не быть доступа к реализации любой из маленьких функций, которые из call_smth() зовутся. J>А это значит, что размер контейнера внутри этой неизвестной маленькой функции может измениться и кешировать его нельзя.
В большинстве популярных компиляторов (в обеих трёх) есть возможность указать опцию оптимизации, суть которой состоит в том, что на объект нет альтернативных ссылок. То есть если ты получил сонтейнер по ссылке и никуда дальше его не передаёшь, то компилятор может считать, что никто дальше его не меняет.
В любом случае, обычно компилятор формальные проверки делает лучше, чем человек. Так что писать for( int i = 0; i < c.size(); i++) { body(); } НАДЁЖНЕЕ!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Выносить вычисление длины выше цикла уже не нужно?
C>Если _End или _Begin отмечены как volatile — обязан прочитать новые значения при очередном вызове size()
Если они так помечены не без оснований, то лучше и не мешать компилятору пересчитывать size() на каждой итерации
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Выносить вычисление длины выше цикла уже не нужно?
Здравствуйте, Erop, Вы писали:
E>В большинстве популярных компиляторов (в обеих трёх) есть возможность указать опцию оптимизации, суть которой состоит в том, что на объект нет альтернативных ссылок. То есть если ты получил сонтейнер по ссылке и никуда дальше его не передаёшь, то компилятор может считать, что никто дальше его не меняет.
Если ты о strict aliasing, то это немного не о том. Там опция компиляции говорит только, что на одну и ту же память нет ссылок _разных_ типов. А у тебя замечательно может быть один и тот же тип и все обломится (в сымсле, ничего оптимизироваться не будет).
E>В любом случае, обычно компилятор формальные проверки делает лучше, чем человек. Так что писать for( int i = 0; i < c.size(); i++) { body(); } НАДЁЖНЕЕ!
Если не волнует скорость — конечно. Пусть всё по 100500 раз надежно позовет.
Здравствуйте, minorlogic, Вы писали:
M>Здравствуйте, jazzer, Вы писали:
J>>Не поможет. J>>доступ должен быть ко всему — и к size, и к телу цикла целиком (со всеми вложенными вызовами), иначе кешировать нельзя.
M>Да , я про такую ситуацию.
Ок. Ну и поскольку гарантировать доступность всего дерева крайне трудно, проще и надежнее закешировать самостоятельно.
Здравствуйте, jazzer, Вы писали:
J>Если ты о strict aliasing, то это немного не о том. Там опция компиляции говорит только, что на одну и ту же память нет ссылок _разных_ типов. А у тебя замечательно может быть один и тот же тип и все обломится (в сымсле, ничего оптимизироваться не будет).
Мне кажется, что ты заблуждаешься. Но я не готов сейчас проверять, что текущеи версии компиляторов при включений этой опции таки выносят .size() из цикла
J>Если не волнует скорость — конечно. Пусть всё по 100500 раз надежно позовет.
Всё-таки это надо какой-то мегабыстрый цикл написать, чтобы от того, вынесут .end из цикла или нет что-то зависело
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском