Разница в производительности между итератором ++ и итератором ++?

Мой помощник утверждает, что для типов объектов preincrement более эффективен, чем post increment

например.

std::vector<std::string> vec;

... insert a whole bunch of strings into vec ...

// iterate over and do stuff with vec.  Is this more efficient than the next 
// loop?
std::vector<std::string>::iterator it;
for (it = vec.begin(); it != vec.end(); ++it){

}

// iterate over and do stuff with vec.  Is this less efficient than the previous loop?
std::vector<std::string>::iterator it;
for (it = vec.begin(); it != vec.end(); it++){

}

Ответ 1

Postincrement должен возвращать значение, которое итератор имел до того, как он увеличивался; так что предыдущее значение нужно скопировать где-нибудь, прежде чем изменять его с помощью соответствующего приращения, чтобы он был доступен для возврата. Дополнительная работа может быть немного или много, но она, конечно, не может быть меньше нуля, по сравнению с преинкрементом, которая может просто выполнить приращение, а затем вернуть только что измененное значение - без копирования//сохранения//и т.д.

Итак, если вы специально НЕ ДОЛЖНЫ иметь постинкремент (потому что вы каким-то образом используете значение до инкремента), вы всегда должны использовать preincrement.

Ответ 2

Операторы приращения по умолчанию будут выглядеть так:

class X
{
    X operator++(int)  // Post increment
    {
        X  tmp(*this);
        this->operator++(); // Call pre-increment on this.
        return tmp;
    }
    X& operator++()  // Pre increment
    {
        // Do operation for increment.
        return *this;
    }
};

Обратите внимание, что пост-инкремент определяется в терминах предварительного приращения. Таким образом, Post-increment выполняет ту же работу плюс дополнительные. Обычно это конструирует копию, а затем возвращает копию через копию (обратите внимание, что Pre-increment может возвращать себя по ссылке, но Post-increment не может).

Ответ 3

Для примитивных типов я использовал это. Это помогло мне получить 10% -ное повышение во многих программах на Visual Studio еще в 1998-1999 годах. Вскоре после этого они исправили свой оптимизатор. Оператор post increment создал переменную в стеке, скопировал значение, увеличил исходный код и затем удалил переменную из стека. Как только оптимизаторы обнаружили, что он не использовался, пособие ушло.

Большинство людей не прекращали кодирование таким образом.:) Мне понадобилось несколько лет.

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

Jacob

Ответ 4

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

Ответ 5

Серьезно, если у вас нет проблем с производительностью из-за вызова пост-инкремента вместо предварительного приращения РАЗЛИЧИЕ РАЗЛИЧИЯ БУДЕТ ТОЛЬКО В ОЧЕНЬ РЕДКИХ ОБСТОЯТЕЛЬСТВАХ.


Обновление

Например, если ваше программное обеспечение является климатическим симулятором, а operator++ заставляет симуляцию двигаться вперед (т.е. ++simulation дает вам следующий шаг в симуляции, а simulation++ делает копию текущий шаг в моделировании, продвигает симуляцию, а затем передает вам копию), тогда производительность будет проблемой.

Более реалистично, в комментарии, первоначальный вопросник упоминает, что мы обсуждаем встроенный проект с (по-видимому) требованиями к реальному времени. В этом случае вам нужно действительно посмотреть на вашу конкретную реализацию. Я серьезно сомневаюсь, что у вас будут заметные замедления, повторяющиеся с помощью std::vector с пост-инкрементом вместо предварительного приращения, хотя я не сравнивал какую-либо реализацию STL по этому вопросу.


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

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

Ответ 7

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

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

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

Я: Это здорово! Это означает, что почти все ваше время идет на увеличение итераторов. Посмотрите выше стека и посмотрите, откуда он. Замените это простым приращением целых чисел, и вы получите огромное ускорение.

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