Я читал, что я должен избегать постфиксного оператора приращения из-за соображений производительности (в некоторых случаях).
Но не влияет ли это на читаемость кода? По моему мнению:
for(int i = 0; i < 42; i++);
/* i will never equal 42! */
Выглядит лучше чем:
for(int i = 0; i < 42; ++i);
/* i will never equal 42! */
Но это, вероятно, просто по привычке. По общему признанию, я не видел много использования ++i
.
Является ли производительность настолько плохой, чтобы жертвовать читабельностью, в этом случае? Или я просто слепой и ++i
читабельнее чем i++
?
c++
readability
postfix
Матин Улхак
источник
источник
i++
прежде чем я знал, что это может повлиять на производительность++i
, поэтому я переключился. Сначала последнее выглядело немного странно, но через некоторое время я привык к нему, и теперь это кажется таким же естественным, какi++
.++i
иi++
делать разные вещи в определенных контекстах, не думайте, что они одинаковы.for (type i = 0; i != 42; ++i)
. Не только можетoperator++
быть перегружен, но так можетoperator!=
иoperator<
. Приращение префикса не дороже, чем постфикс, неравенство не дороже, чем меньше. Какие из них мы должны использовать?Ответы:
Факты:
i ++ и ++ одинаково легко читаются. Он вам не нравится, потому что вы к нему не привыкли, но, по сути, вы не можете его неправильно истолковать, так что читать и писать больше не нужно.
По крайней мере, в некоторых случаях постфиксный оператор будет менее эффективным.
Тем не менее, в 99,99% случаев это не имеет значения, потому что (а) он будет работать с простым или примитивным типом в любом случае, и это проблема, только если он копирует большой объект; (б) он не будет работать критическая часть кода (с), вы не знаете, оптимизирует ли это компилятор или нет, это может сделать.
Таким образом, я предлагаю использовать префикс, если вам не нужен постфикс - это хорошая привычка, потому что (а) это хорошая привычка быть точным с другими вещами и (б) однажды в синей луне вы намереваетесь использовать постфикс и поймите это неправильно: если вы всегда пишете, что имеете в виду, это менее вероятно. Всегда есть компромисс между производительностью и оптимизацией.
Вы должны руководствоваться здравым смыслом, а не микрооптимизировать, пока вам это не нужно, но ни один из них не должен быть явно неэффективным ради этого. Как правило, это означает: во-первых, исключить любую конструкцию кода, которая неприемлемо неэффективна даже в некритическом коде (обычно что-то, представляющее фундаментальную концептуальную ошибку, такую как передача 500-мегабайтных объектов по значению без причины); и во-вторых, из всех других способов написания кода, выберите самый ясный.
Тем не менее, здесь, я полагаю, ответ прост: я считаю, что написание префикса, если только вам конкретно не нужен постфикс, (а) очень незначительно яснее и (б) очень незначительно с большей вероятностью будет более эффективным, поэтому вы всегда должны писать это по умолчанию, но не беспокойся об этом, если забудешь.
Шесть месяцев назад я думал так же, как и вы, что i ++ был более естественным, но это чисто то, к чему вы привыкли.
РЕДАКТИРОВАТЬ 1: Скотт Мейерс, в "Более эффективном C ++", которому я обычно доверяю в этом вопросе, говорит, что в общем следует избегать использования оператора postfix для пользовательских типов (поскольку единственная разумная реализация функции приращения postfix - это создание копии объекта, вызовите функцию приращения префикса для выполнения приращения и верните копию, но операции копирования могут быть дорогими).
Итак, мы не знаем, существуют ли какие-либо общие правила относительно (а) того, верно ли это сегодня, (б) применимо ли оно (в меньшей степени) к внутренним типам (в) следует ли использовать «++» для что-нибудь большее, чем легкий класс итераторов. Но по всем причинам, которые я описал выше, не имеет значения, делай то, что я сказал раньше.
РЕДАКТИРОВАТЬ 2: Это относится к общей практике. Если вы думаете, что это имеет значение в каком-то конкретном случае, то вам следует профилировать его и посмотреть. Профилирование легко и дешево и работает. Исходя из первых принципов, то, что нужно оптимизировать, сложно, дорого и не работает.
источник
i++
а не кроется++i
в названии определенного популярного языка программирования, упомянутого в этом вопросе / ответе ...Всегда кодируйте сначала программиста, а компьютера - второго.
Если есть разница в производительности, после того, как компилятор внимательно изучит ваш код, и вы сможете измерить его и это важно - тогда вы можете его изменить.
источник
GCC выдает одинаковый машинный код для обоих циклов.
Код C
Код сборки (с моими комментариями)
источник
Теперь, когда это вне нашего пути, давайте сделаем наш выбор разумно :
++i
: приращение префикса , увеличение текущего значения и получение результатаi++
: увеличение постфикса , копирование значения, увеличение текущего значения, получение копииЕсли не требуется копия старого значения, использование постфиксного приращения - это обходной способ добиться цели.
Неточность проистекает из лени, всегда используйте конструкцию, которая выражает ваши намерения самым прямым образом, есть меньше шансов, чем будущий сопровождающий может неправильно понять ваше первоначальное намерение.
Даже при том, что это (действительно) незначительно, бывают случаи, когда я действительно озадачен чтением кода: мне действительно было интересно, совпадали ли намерение и фактическое выражение, и, конечно, через несколько месяцев они (или я) тоже не помню ...
Таким образом, не имеет значения, выглядит ли это правильно для вас или нет. Обними поцелуй . Через несколько месяцев вы избежите своих старых практик.
источник
В C ++ вы можете существенно изменить производительность, если задействованы перегрузки операторов, особенно если вы пишете шаблонный код и не знаете, какие итераторы могут быть переданы. Логика любого итератора X может быть как существенной, так и значимой. то есть медленный и неоптимизируемый компилятором.
Но это не так в C, где вы знаете, что это будет только тривиальный тип, а разница в производительности тривиальна, и компилятор может легко оптимизировать.
Итак, совет: вы программируете на C или C ++, и вопросы касаются одного или другого, а не обоих.
источник
Производительность любой операции сильно зависит от базовой архитектуры. Нужно увеличивать значение, которое хранится в памяти, что означает, что узкое место фон Неймана является ограничивающим фактором в обоих случаях.
В случае ++ i, мы должны
В случае i ++ мы должны
Операторы ++ и - ведут свое происхождение к набору команд PDP-11. PDP-11 может выполнять автоматическое постинкрементное увеличение регистра. Он также может выполнять автоматическое предварительное уменьшение эффективного адреса, содержащегося в регистре. В любом случае компилятор мог воспользоваться этими операциями машинного уровня только в том случае, если рассматриваемая переменная была переменной «регистр».
источник
Если вы хотите знать, если что-то медленно, проверьте это. Возьмите BigInteger или его эквивалент, вставьте его в похожий цикл for с использованием обоих идиом, убедитесь, что внутренняя часть цикла не оптимизирована, и рассчитайте их время.
Прочитав статью, я не нахожу ее очень убедительной по трем причинам. Во-первых, компилятор должен иметь возможность оптимизировать создание объекта, который никогда не используется. Во-
i++
вторых , эта концепция идиоматична для числовых циклов for , поэтому случаи, на которые я могу обратить внимание, ограничены. В-третьих, они дают чисто теоретический аргумент, без цифр, подтверждающих это.Основываясь на причине № 1, я полагаю, что когда вы действительно сделаете выбор времени, они будут рядом друг с другом.
источник
Прежде всего это не влияет на читабельность ИМО. Это не то, что вы привыкли видеть, но пройдет совсем немного времени, прежде чем вы привыкнете к этому.
Во-вторых, если вы не используете тонну постфиксных операторов в своем коде, вы вряд ли увидите большую разницу. Основным аргументом для того, чтобы не использовать их, когда это возможно, является то, что копия оригинального значения переменной var должна храниться до конца аргументов, в которых еще можно использовать исходный аргумент var. Это 32 или 64 бит в зависимости от архитектуры. Это соответствует 4 или 8 байтов, или 0,00390625, или 0,0078125 МБ. Шансы очень высоки, что если вы не используете тонну из них, которые нужно сохранять в течение очень длительного периода времени, при сегодняшних компьютерных ресурсах и скорости, вы даже не заметите разницу при переходе с постфикса на префикс.
РЕДАКТИРОВАТЬ: Забудьте эту оставшуюся часть, так как мой вывод оказался ложным (за исключением части ++ i и i ++, которые не всегда делают одно и то же ... это все еще верно).
Также было указано ранее, что они не делают то же самое в случаях. Будьте осторожны при переключении, если решите. Я никогда не пробовал (я всегда использовал postfix), поэтому я не знаю наверняка, но я думаю, что переход с postfix на prefix приведет к другим результатам: (опять же, я могу ошибаться ... зависит от компилятора / переводчик тоже)
источник
Я думаю, что семантически,
++i
имеет больше смысла, чемi++
, поэтому я бы придерживался первого, за исключением того, что обычно это не делается (как в Java, где вы должны использовать,i++
потому что он широко используется).источник
Это не только производительность.
Иногда вы хотите вообще избежать копирования, потому что это не имеет смысла. И поскольку использование приращения префикса не зависит от этого, проще придерживаться формы префикса.
И использование разных приращений для примитивных типов и сложных типов ... это действительно нечитаемо.
источник
Если вам это действительно не нужно, я бы придерживался ++ i. В большинстве случаев это то, что каждый намерен. Я не очень часто нуждаюсь в i ++, и вам всегда нужно дважды подумать, читая такую конструкцию. С ++ i это легко: вы добавляете 1, используете его, а затем я все тот же.
Итак, я полностью согласен с @martin beckett: сделай это проще для себя, это уже достаточно сложно.
источник