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

Единственное преимущество случая if over - это когда наблюдается заметное увеличение частоты появления первого случая.

Не уверен, где именно находится порог, но я использую синтаксис case, если первый «почти всегда» не проходит первый тест.

432
задан Mark Harrison 8 February 2015 в 22:06
поделиться

11 ответов

Резюме: Нет.

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

Мы можем продемонстрировать это путем рассмотрения кода для этой функции, и с ++i и i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

файлы являются тем же, за исключением ++i и i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

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

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

И мы видим, что и сгенерированные объектные и ассемблерные файлы являются тем же.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
364
ответ дан Ziezi 8 February 2015 в 22:06
поделиться

Мой C немного ржав, таким образом, я приношу извинения заранее. Speedwise, я могу понять результаты. Но, я смущен относительно того, как оба файла вышли к тому же хешу MD5. Возможно, для цикла выполняет то же, но не был бы следующие 2 строки кода генерировать различный блок?

myArray[i++] = "hello";

по сравнению с

myArray[++i] = "hello";

первый пишет значение в массив, затем увеличивает i. Вторые инкременты i тогда записи к массиву. Я не эксперт по блоку, но я просто не вижу, как тот же исполняемый файл был бы сгенерирован этими 2 различными строками кода.

Просто мои два цента.

0
ответ дан Jason Z 8 February 2015 в 22:06
поделиться

В C компилятор может обычно оптимизировать их, чтобы быть тем же, если результат не использован.

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

5
ответ дан Kristopher Johnson 8 February 2015 в 22:06
поделиться

Вот дополнительное наблюдение, если Вы волнуетесь по поводу микро оптимизации. Постепенное уменьшение циклов может 'возможно' быть более эффективным, чем постепенное увеличение циклов (в зависимости от архитектуры системы команд, например, ARM), данный:

for (i = 0; i < 100; i++)

На каждом цикле Вы у Вас будет одна инструкция каждым для:

  1. Добавление 1 к i.
  2. Выдерживают сравнение, является ли i меньше, чем 100.
  3. условный переход А, если i меньше, чем 100.

принимая во внимание, что постепенно уменьшающийся цикл:

for (i = 100; i != 0; i--)

цикл будет иметь инструкцию для каждого из:

  1. Декремент i, устанавливая ЦП регистрируют флаг состояния.
  2. условный переход А в зависимости от ЦП регистрируют состояние (Z==0).

, Конечно, это работает только при постепенном уменьшении для обнуления!

Помнил от Руководства Разработчика системы ARM.

16
ответ дан Ziezi 8 February 2015 в 22:06
поделиться

От Эффективность по сравнению с намерением Andrew Koenig:

Первый, совсем не очевидно, что ++i более эффективно, чем i++, по крайней мере, где целочисленные переменные затронуты.

И:

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

Так, если бы получающееся значение не используется, я использовал бы ++i. Но не потому что это более эффективно: потому что это правильно указывает мое намерение.

101
ответ дан Ziezi 8 February 2015 в 22:06
поделиться

Взятие листа от Scott Meyers, Более эффективный C++ Объект 6: Различайте префикс и снабдите постфиксом формы инкрементных и декрементных операций .

версия префикса всегда предпочитается по постфиксу в отношении объектов, особенно в отношении итераторов.

причина этого, если Вы смотрите на шаблон вызова операторов.

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

Рассмотрение этого примера легко видеть, как префиксный оператор всегда будет более эффективным, чем постфикс. Из-за потребности во временном объекте в использовании постфикса.

Поэтому, когда Вы видите, что примеры используют итераторы, они всегда используют версию префикса.

, Но как Вы указываете для интервала, нет эффективно никакого различия из-за оптимизации компилятора, которая может произойти.

16
ответ дан JProgrammer 8 February 2015 в 22:06
поделиться

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

Использование, какой бы ни имеет большую часть смысла человеку, читающему код.

11
ответ дан Andy Lester 8 February 2015 в 22:06
поделиться

Лучший ответ - то, что ++i иногда будет быстрее, но никогда медленнее.

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

Однако, если i составной тип тогда, можно найти измеримое различие. Для i++ необходимо сделать копию класса прежде, чем увеличить его. В зависимости от того, что вовлечено в копию, это могло действительно быть медленнее с тех пор с ++it, можно просто возвратить окончательное значение.

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

Другое различие - то, что с ++i у Вас есть опция возврата ссылки вместо значения. Снова, в зависимости от того, что вовлечено в создание копии Вашего объекта, это могло быть медленнее.

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

43
ответ дан Ziezi 8 February 2015 в 22:06
поделиться

Я всегда предпочитаю преинкремент, однако...

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

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

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

2
ответ дан 8 February 2015 в 22:06
поделиться

@Mark Даже при том, что компилятору позволяют оптимизировать далеко (базирующийся стек) временная копия переменной и gcc (в последних версиях) делает так, не означает весь , компиляторы будут всегда делать так.

я просто протестировал его с компиляторами, которые мы используем в нашем текущем проекте, и 3 из 4 не оптимизируют его.

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

, Если у Вас нет действительно глупой реализации одного из операторов в Вашем коде:

Alwas предпочитают ++ я по мне ++.

7
ответ дан Andreas 9 February 2015 в 08:06
поделиться

Я прочитывал большинство ответов здесь и многие комментарии, и я не видел ссылки на одна экземпляр, о котором я мог думать, где i++ более эффективно, чем ++i (и возможно удивительно --i был более эффективны, чем i--). Это для компиляторов C для PDP-11 DEC!

PDP-11 имел инструкции по сборке для преддекремента регистра и постинкремента, но не наоборот. Инструкции позволили любому регистру "общего назначения" использоваться в качестве указателя вершины стека. Таким образом, при использовании чего-то как *(i++), это могло бы быть скомпилировано в единственную инструкцию по сборке, в то время как *(++i) не мог.

Это - очевидно, очень тайный пример, но он действительно обеспечивает исключение, где постинкремент более эффективен (или я должен сказать , был , так как нет большого спроса на PDP-11 C кода в эти дни).

4
ответ дан 22 November 2019 в 23:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: