Имеет ли смысл оптимизировать i ++ как ++ i, чтобы избежать временной переменной? - PullRequest
18 голосов
/ 16 сентября 2010

Кто-то сказал мне, что я могу написать

for (iterator it = somecontainer.begin(); it != somecontainer.end(); ++it)

вместо

for (iterator it = somecontainer.begin(); it != somecontainer.end(); it++)

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

Ответы [ 7 ]

33 голосов
/ 16 сентября 2010

Это хорошая привычка, поскольку итераторы могут быть сколь угодно сложными.Для индексов vector::iterator или int нет, это не будет иметь значения.

Компилятор никогда не сможет удалить (исключить) копию, поскольку удаление из копии исключает только промежуточные временные, а не неиспользованные временные.Для легких объектов, включая большинство итераторов, компилятор может оптимизировать код, реализующий копию.Однако, это не всегда очевидно, когда это невозможно.Например, постинкрементное istream_iterator<string> - это гарантированное для копирования последнего string прочитанного объекта.В реализации string без учета ссылок, которая будет выделять, копировать и немедленно освобождать память.Эта проблема еще более вероятна для более тяжелых классов без итераторов, поддерживающих постинкремент.

Нет, конечно, недостатка .Похоже, что он стал доминирующим стилем за последние десять или два года.

7 голосов
/ 16 сентября 2010

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

Но я сомневаюсь, что стоит вернуться и изменить существующий код для использования префиксной версии.

6 голосов
/ 16 сентября 2010

Да, это концептуально правильно. Вас волнует, если это i++ или ++i? Нет, ты не Какой из них лучше? Второй лучше, так как он потенциально быстрее. Таким образом, вы выбираете второе (предварительное увеличение).

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

6 голосов
/ 16 сентября 2010

Нет.(Стилистически я предпочитаю это, потому что мой родной язык - английский, в основном это язык глагола-предшествующего-существительного, и поэтому "increment it" читается легче, чем "it increment". Но этостиль и субъективность.)

Однако, если вы не изменяете содержимое somecontainer в своем цикле, вы можете рассмотреть возможность получения возвращаемого значения somecontainer.end() во временную переменную.То, что вы там делаете, будет вызывать функцию в каждом цикле.

3 голосов
/ 16 сентября 2010

Рассмотрим, как постинкремент обычно реализуется в определяемом пользователем типе:

MyIterator operator++(int) {
    MyIterator retval(*this);
    ++*this;
    return retval;
}

Итак, у нас есть два вопроса: может мой компилятор оптимизировать это в случаях, когда возвращаемое значение не используется, и будет мой компилятор оптимизировать это в тех случаях, когда возвращаемое значение не используется?

Что касается "может ли это?", То, безусловно, бывают случаи, когда это невозможно. Если код не указан, нам не повезло. Если конструктор копирования MyIterator имеет наблюдаемые побочные эффекты, то нам не повезло - elision конструктора копирования позволяет создавать возвращаемые значения и копии временных объектов, поэтому, по крайней мере, значение может быть скопировано только один раз. Но это не позволяет возвращаемым значениям вообще не создаваться. В зависимости от вашего компилятора, выделение памяти вполне может быть заметным побочным эффектом.

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

Что касается "будет ли это?" Попробуйте это на вашем компиляторе. Я не могу быть обеспокоен, потому что я всегда использую предварительное увеличение, где два эквивалентны; -)

Что касается «нужно ли учитывать эту оптимизацию при написании кода?», Я бы сказал, что это не так, но преждевременная пессимизация так же вредна, как и преждевременная оптимизация. Тот факт, что один (в лучшем случае) является пустой тратой времени, не означает, что другой благороден - не пытайтесь сделать свой код медленнее только потому, что можете. Если кто-то искренне предпочитает видеть i++ в цикле, то вряд ли когда-либо замедлит его код, поэтому он может оптимизировать для эстетики, если это необходимо. Лично я предпочел бы, чтобы люди улучшали свой вкус ...

3 голосов
/ 16 сентября 2010

Это зависит от типа, к которому вы применяете operator++.Если вы говорите о пользовательском типе, это будет включать в себя копирование всего UDT, что может быть очень дорогим.

Однако, если вы говорите о встроенном типе, то, скорее всего, нет никакой разницы.

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

1 голос
/ 16 сентября 2010

Это на самом деле изменит неоптимизированный код, то есть отладочные сборки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...