Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Есть ли разница в производительности между i++ и ++i в C?
Есть ли разница в производительности между i++ и ++i , если результирующее значение не используется?
Резюме: нет.
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
Из книги Эндрю Кенига " эффективность против намерения " :
Во-первых, далеко не очевидно , что
++iявляется более эффективным, чемi++, по крайней мере, когда речь идет о целочисленных переменных.
И :
Поэтому вопрос, который следует задать, заключается не в том, какая из этих двух операций быстрее, а в том, какая из этих двух операций точнее выражает то, что вы пытаетесь выполнить. Я утверждаю, что если вы не используете значение выражения , то нет никакой причины использовать
i++вместо++i, потому что нет никакой причины копировать значение переменной, увеличивать переменную, а затем выбрасывать копию.
Поэтому, если результирующее значение не используется, я бы использовал ++i . Но не потому, что он более эффективен: потому что он правильно формулирует мое намерение.
Лучший ответ заключается в том, что ++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++ , когда результат не влияет на результат.
Взяв лист у Скотта Мейерса, более эффективный c++ пункт 6: различайте префиксные и постфиксные формы операций инкремента и декремента .
Префиксная версия всегда предпочтительнее постфиксной в отношении объектов, особенно в отношении итераторов.
Причина этого, если посмотреть на схему вызова операторов.
// Prefix
Integer& Integer::operator++()
{
*this += 1;
return *this;
}
// Postfix
const Integer Integer::operator++(int)
{
Integer oldValue = *this;
++(*this);
return oldValue;
}
Глядя на этот пример, легко увидеть, что префиксный оператор всегда будет более эффективным, чем постфиксный. Из-за необходимости временного объекта в использовании постфикса.
Именно поэтому, когда вы видите примеры использования итераторов, они всегда используют префиксную версию.
Но, как вы указываете для int, фактически нет никакой разницы из-за оптимизации компилятора, которая может иметь место.
Вот дополнительное наблюдение, если вы беспокоитесь о микрооптимизации. Декрементирующие циклы могут быть 'possibly' более эффективными, чем инкрементирующие циклы (в зависимости от архитектуры набора команд, например ARM), учитывая:
for (i = 0; i < 100; i++)
На каждом цикле вы будете иметь по одной инструкции для каждого:
- Добавление
1кi. - Сравните, является ли
iменьше, чем100. - Условная ветвь, если
iменьше, чем100.
В то время как цикл уменьшения:
for (i = 100; i != 0; i--)
Цикл будет иметь инструкцию для каждого из них.:
- Декремент
i, установка флага состояния регистра CPU. - Условная ветвь, зависящая от состояния регистра CPU (
Z==0).
Конечно, это работает только при уменьшении до нуля!
Вспомнил из руководства разработчика системы ARM.
Короткий ответ :
Между i++ и ++i никогда не бывает разницы в скорости. Хороший компилятор не должен генерировать разный код в этих двух случаях.
Длинный ответ:
Все остальные ответы не упоминают о том, что различие между ++i и i++ имеет смысл только в том выражении, в котором оно найдено.
В случае с for(i=0; i<n; i++) i++ является единственным в своем собственном выражении: есть точка последовательности Перед i++ и есть еще одна после него. Таким образом, единственный генерируемый машинный код-это "увеличение i на 1", и он четко определен, как это происходит по отношению к rest программы. Поэтому, если вы измените его на префикс ++, это не будет иметь ни малейшего значения, вы все равно получите машинный код "увеличить i на 1 ".
Различия между ++i и i++ имеют значение только в таких выражениях, как array[i++] = x; и array[++i] = x; . Некоторые могут возразить и сказать, что постфикс будет медленнее в таких операциях, потому что регистр, где находится i , должен быть перезагружен позже. Но затем обратите внимание, что компилятор волен упорядочивать ваши инструкции так, как ему заблагорассудится, до тех пор, пока он не делает "break the behavior of the abstract machine", как это называется в стандарте C.
Поэтому, хотя вы можете предположить, что array[i++] = x; переводится в машинный код как:
- Хранить значение
iв регистре A. - Адрес массива в регистр В.
- Добавьте A и B, сохраните результаты в A.
- В этом новом адресе, представленном A, сохраните значение x.
- Хранить значение
iв регистре a / / неэффективно, потому что дополнительная инструкция здесь, мы уже делали это однажды. - Инкрементный регистр А.
- Зарегистрировать магазин в
i.
компилятор может также производить код более эффективно, например:
- Хранить значение
iв регистре A. - Адрес массива в регистр В.
- Добавьте A и B, сохраните результаты в B.
- Инкрементный регистр А.
- Зарегистрировать магазин в
i. ... - // rest кодекса.
Просто потому, что вы, как программист C, обучены думать, что постфикс ++ происходит в конце, машинный код не должен быть упорядочен таким образом.
Таким образом, нет никакой разницы между префиксом и постфиксом ++ в C. Теперь то, что вы как программист C должны быть в курсе, это люди, которые непоследовательно используют префикс в одних случаях и постфикс в других случаях, без какого-либо обоснования почему. Это говорит о том, что они не уверены в том, как работает C, или что они неверно знают язык. Это всегда плохой знак, он, в свою очередь, предполагает, что они принимают другие сомнительные решения в своей программе, основанные на суеверии или "religious dogmas".
"Префикс ++ всегда быстрее" -это действительно одна из таких ложных догм, которая распространена среди потенциальных программистов C.
Пожалуйста, не позволяйте вопросу о "which one is faster" быть решающим фактором, который нужно использовать. Скорее всего, вы никогда не будете так сильно беспокоиться, и, кроме того, время чтения программиста намного дороже, чем машинное время.
Используйте то, что имеет наибольший смысл для человека, читающего код.
Во-первых: разница между i++ и ++i в C году ничтожна.
К деталям.
1. Хорошо известная проблема C++: ++i быстрее
В C++, ++i является более эффективным, если i является своего рода объектом с перегруженным оператором инкремента.
Почему?
В ++i объект сначала инкрементируется, а затем может передаваться как ссылка const на любую другую функцию. Это невозможно, если выражение имеет значение foo(i++) , потому что теперь инкремент должен быть выполнен до вызова foo() , но старое значение должно быть передано в foo() . Следовательно, компилятор вынужден сделать копию i , прежде чем он выполнит оператор инкремента на оригинале. Дополнительные вызовы конструктора / деструктора-это плохая часть.
Как отмечалось выше, это не относится к фундаментальным типам.
2. Малоизвестный факт: i++ может быть быстрее
Если нет необходимости вызывать конструктор/деструктор, что всегда происходит в C, ++i и i++ , то это должно быть одинаково быстро, верно? № Они практически одинаково быстры, но могут быть небольшие различия, которые большинство других респондентов неправильно поняли.
Как i++ может быть быстрее?
Дело в зависимостях данных. Если значение должно быть загружено из памяти, необходимо выполнить с ним две последующие операции: увеличить его и использовать. При использовании параметра ++i необходимо выполнить инкрементацию, прежде чем можно будет использовать это значение. В случае i++ использование не зависит от инкремента, и CPU может выполнять операцию использования параллельно операции инкремента. Разница составляет не более одного цикла CPU, так что она действительно ничтожна, но она есть. И это совсем другой путь, чем многие ожидали бы.
@Mark Несмотря на то, что компилятор может оптимизировать удаленную временную копию переменной (на основе стека) и gcc (в последних версиях) делает это, это не значит, что все компиляторы всегда будут так поступать.
Я только что протестировал его с помощью компиляторов, которые мы используем в нашем текущем проекте, и 3 из 4 не оптимизируют его.
Никогда не предполагайте, что компилятор делает это правильно, особенно если возможно более быстрый, но никогда не медленный код так же легко читается.
Если у вас нет действительно глупой реализации одного из операторов в вашем коде:
Alwas предпочитают ++i над i++.
В C компилятор обычно может оптимизировать их, чтобы они были одинаковыми, если результат не используется.
Однако в C++ при использовании других типов, которые предоставляют свои собственные операторы++, префиксная версия, скорее всего, будет быстрее, чем постфиксная версия. Поэтому, если вам не нужна постфиксная семантика, лучше использовать префиксный оператор.
Я могу представить себе ситуацию, когда постфикс медленнее, чем префикс инкремента:
Представьте себе, что процессор с регистром A используется в качестве аккумулятора, и это единственный регистр, используемый во многих инструкциях (некоторые небольшие микроконтроллеры на самом деле таковы).
Теперь представьте себе следующую программу и их перевод в гипотетический assembly:
Инкремент префикса:
a = ++b + c;
; increment b
LD A, [&b]
INC A
ST A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
Постфиксный инкремент:
a = b++ + c;
; load b
LD A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
; increment b
LD A, [&b]
INC A
ST A, [&b]
Обратите внимание, как значение b было принудительно перезагружено. С префиксным инкрементом компилятор может просто увеличить значение и продолжить его использование, возможно, избегая перезагрузки, так как желаемое значение уже находится в регистре после инкремента. Однако с постфиксным приращением компилятор должен иметь дело с двумя значениями, одним старым и одним увеличенным значением, которое, как я показал выше, приводит к еще одному доступу к памяти.
Конечно, если значение инкремента не используется, например, один оператор i++; , компилятор может (и делает) просто генерировать инструкцию инкремента независимо от использования постфикса или префикса.
В качестве дополнительного замечания я хотел бы отметить, что выражение, в котором есть b++ , не может быть просто преобразовано в одно с ++b без каких-либо дополнительных усилий (например, добавив - 1 ). Таким образом, сравнение этих двух, если они являются частью некоторого выражения, на самом деле не является корректным. Часто, когда вы используете b++ внутри выражения, вы не можете использовать ++b , поэтому даже если бы ++b был потенциально более эффективным, это было бы просто неправильно. Исключение составляет, конечно, если выражение просится на него (например, a = b++ + 1; , который может быть изменен на a = ++b;).
Однако я всегда предпочитаю предварительное приращение ...
Я хотел бы отметить, что даже в случае вызова функции operator++ компилятор сможет оптимизировать временную часть, если функция будет встроена. Поскольку оператор++ обычно короткий и часто реализуется в заголовке, он, скорее всего, будет встроен.
Таким образом, для практических целей, скорее всего, нет большой разницы между эффективностью этих двух форм. Однако я всегда предпочитаю предварительное приращение, поскольку мне кажется, что лучше прямо выразить то, что я пытаюсь сказать, а не полагаться на оптимизатор, чтобы понять это.
Кроме того, давая optmizer меньше делать, скорее всего, означает, что компилятор работает быстрее.
Мой C немного заржавел, так что заранее приношу свои извинения. Быстро, я могу понять результаты. Но я запутался в том, как оба файла вышли на один и тот же MD5 hash. Может быть, цикл for работает так же, но не будут ли следующие 2 строки кода генерировать разные assembly?
myArray[i++] = "hello";
против
myArray[++i] = "hello";
Первый из них записывает значение в массив, а затем увеличивает i. второй инкремент i затем записывает в массив. Я не эксперт assembly, но я просто не вижу, как один и тот же исполняемый файл будет генерироваться этими двумя разными строками кода.
Только мои два цента.