Нет, но да, но, может быть, но, может быть, по-другому, но нет.
Как уже указывали люди (если принять язык, в котором сложение является левоассоциативным, например C, C ++, C # или Java), выражение ((1 + 2) + 3)
в точности эквивалентно 1 + 2 + 3
. Это разные способы написания чего-либо в исходном коде, что будет иметь нулевой эффект на результирующий машинный код или байт-код.
В любом случае результатом будет инструкция, например, добавить два регистра и затем добавить третий, или взять два значения из стека, добавить его, вытолкнуть обратно, затем взять это и другое и добавить их, или добавить три регистра в одна операция или какой-либо другой способ суммирования трех чисел в зависимости от того, что наиболее разумно на следующем уровне (машинный код или байт-код). В случае с байтовым кодом, который, в свою очередь, вероятно, подвергнется аналогичной реструктуризации, например, в эквиваленте этого для IL (который будет представлять собой серию загрузок в стек и добавление пар для добавления и последующего возврата результата) не приведет к прямому копированию этой логики на уровне машинного кода, а к чему-то более разумному для рассматриваемой машины.
Но есть кое-что еще к вашему вопросу.
В случае любого здравомыслящего компилятора C, C ++, Java или C # я ожидаю, что результат обоих приведенных вами утверждений будет иметь точно такие же результаты, как:
int a = 6;
Почему полученный код должен тратить время на математику литералов? Никакие изменения в состоянии программы не остановят результат от 1 + 2 + 3
того 6
, так это то, что должно идти в коде выполняются. В самом деле, может быть, даже не это (в зависимости от того, что вы делаете с этим 6, может быть, мы можем выбросить все это; и даже C # с его философией «не оптимизировать сильно, поскольку джиттер оптимизирует это в любом случае») либо произведет эквивалент int a = 6
или просто выбросить все как ненужное).
Это, однако, приводит нас к возможному расширению вашего вопроса. Учтите следующее:
int a = (b - 2) / 2;
/* or */
int a = (b / 2)--;
и
int c;
if(d < 100)
c = 0;
else
c = d * 31;
/* or */
int c = d < 100 ? 0 : d * 32 - d
/* or */
int c = d < 100 && d * 32 - d;
/* or */
int c = (d < 100) * (d * 32 - d);
(Обратите внимание, что последние два примера не являются допустимыми в C #, в то время как все остальное здесь и они действительны в C, C ++ и Java.)
Здесь мы снова имеем абсолютно эквивалентный код с точки зрения вывода. Поскольку они не являются константными выражениями, они не будут вычисляться во время компиляции. Возможно, что одна форма быстрее, чем другая. Что быстрее? Это будет зависеть от процессора и, возможно, от некоторых довольно произвольных различий в состоянии (особенно потому, что если он быстрее, он вряд ли будет намного быстрее).
И они не совсем не связаны с вашим вопросом, поскольку они в основном касаются различий в порядке, в котором что-то концептуально делается.
В каждом из них есть причина подозревать, что один может быть быстрее другого. Одиночные декременты могут иметь специальную инструкцию, поэтому (b / 2)--
могут быть быстрее (b - 2) / 2
. d * 32
возможно, будет произведен быстрее, превратив его в d << 5
так, чтобы сделать d * 32 - d
быстрее, чем d * 31
. Различия между двумя последними особенно интересны; одна позволяет пропустить некоторую обработку в некоторых случаях, а другая исключает возможность неправильного прогнозирования ветвлений.
Таким образом, это оставляет нам два вопроса: 1. Является ли один на самом деле быстрее, чем другой? 2. Будет ли компилятор преобразовывать медленный в быстрый?
И ответ 1. Это зависит. 2. Возможно.
Или расширить, это зависит, потому что это зависит от рассматриваемого процессора. Конечно, существуют процессоры, в которых простой машинный код, эквивалентный одному, будет быстрее, чем простой машинный код, эквивалентный другому. На протяжении всей истории электронных вычислений не было ни одного, который всегда был быстрее, либо (элемент неправильного предсказания ветвления, в частности, не относился ко многим, когда неконвейерные ЦП были более распространенными).
И, возможно, поскольку существует множество различных оптимизаций, которые будут выполнять компиляторы (и jitters, и обработчики сценариев), и хотя некоторые из них могут быть обязательными в определенных случаях, мы, как правило, сможем найти некоторые части логически эквивалентного кода, которые даже самый наивный компилятор имеет точно такие же результаты и некоторые фрагменты логически эквивалентного кода, где даже самый сложный код производит более быстрый код для одного, чем для другого (даже если нам нужно написать что-то совершенно патологическое, чтобы доказать свою точку зрения).
Это может показаться очень маленькой проблемой оптимизации,
Нет. Даже с более сложными различиями, чем те, которые я здесь привожу, это кажется абсолютно незначительным беспокойством, которое не имеет ничего общего с оптимизацией. Во всяком случае, это вопрос пессимизации, так как вы подозреваете, что труднее читать ((1 + 2) + 3
может быть медленнее, чем легче читать 1 + 2 + 3
.
но выбор C ++ вместо C # / Java / ... все об оптимизации (IMHO).
Если это действительно то, что выбор C ++ вместо C # или Java был «всем», я бы сказал, что люди должны записать свои копии Stroustrup и ISO / IEC 14882 и освободить пространство своего компилятора C ++, чтобы оставить место для еще нескольких MP3 или чего-то еще.
Эти языки имеют разные преимущества по сравнению друг с другом.
Одним из них является то, что C ++ по-прежнему быстрее и легче в использовании памяти. Да, есть примеры, когда C # и / или Java работают быстрее и / или лучше используют память на протяжении всего жизненного цикла приложения, и они становятся все более распространенными по мере совершенствования соответствующих технологий, но мы все же можем ожидать, что средняя программа, написанная на C ++, будет меньший исполняемый файл, который выполняет свою работу быстрее и использует меньше памяти, чем эквивалент на любом из этих двух языков.
Это не оптимизация.
Оптимизация иногда используется для обозначения «заставить дела идти быстрее». Это понятно, потому что часто, когда мы действительно говорим об «оптимизации», мы действительно говорим о том, чтобы заставить вещи идти быстрее, и поэтому одно стало сокращением для другого, и я признаю, что сам неправильно использовал это слово.
Правильное слово для «ускорения» - это не оптимизация . Правильное слово здесь - улучшение . Если вы вносите изменения в программу, и единственное значимое отличие состоит в том, что она теперь быстрее, она никак не оптимизирована, она просто лучше.
Оптимизация - это когда мы делаем улучшения в отношении конкретного аспекта и / или конкретного случая. Типичные примеры:
- Теперь это быстрее для одного варианта использования, но медленнее для другого.
- Это теперь быстрее, но использует больше памяти.
- Теперь память легче, но медленнее.
- Теперь быстрее, но сложнее в обслуживании.
- Теперь легче поддерживать, но медленнее.
Такие случаи были бы оправданы, если, например:
- Более быстрый вариант использования более распространен или более затруднен для начала.
- Программа была неприемлемо медленной, и у нас много свободной оперативной памяти.
- Программа остановилась из-за того, что использовала столько оперативной памяти, что тратила на нее больше времени, чем на суперскоростную обработку.
- Программа была неприемлемо медленной, а сложный для понимания код хорошо документирован и относительно стабилен.
- Программа по-прежнему приемлемо быстра, а более понятная кодовая база дешевле в обслуживании и позволяет быстрее вносить другие улучшения.
Но такие случаи также не были бы оправданы в других сценариях: код не был улучшен абсолютной безошибочной мерой качества, он был улучшен в определенном отношении, что делает его более подходящим для конкретного использования; оптимизировано.
И выбор языка здесь имеет значение, потому что он может повлиять на скорость, использование памяти и читаемость, но также совместимость с другими системами, доступность библиотек, доступность сред выполнения, зрелость этих сред выполнения в данной операционной системе. (что касается моих грехов, то я почему-то выбрал Linux и Android в качестве своих любимых ОС и C # в качестве моего любимого языка, и хотя Mono великолепен, но я все еще сталкиваюсь с этим совсем немного).
Сказать «выбор C ++ вместо C # / Java / ... это все об оптимизации» имеет смысл, только если вы думаете, что C ++ действительно отстой, потому что оптимизация о «лучше, несмотря на ...», а не «лучше». Если вы думаете, что C ++ лучше, несмотря на саму себя, то последнее, что вам нужно, это беспокоиться о таких мелких микрооптах. В самом деле, вам, вероятно, лучше вообще отказаться от него; счастливые хакеры - это тоже качество для оптимизации!
Если, однако, вы склонны сказать «я люблю C ++, и одна из вещей, которые мне нравятся, это выдавливать лишние циклы», то это другое дело. Это все еще тот случай, когда микроопты того стоят, только если они могут быть рефлексивной привычкой (то есть способ, которым вы склонны кодировать естественным путем, будет быстрее, чем медленнее). В противном случае это даже не преждевременная оптимизация, это преждевременная пессимизация, которая только усугубляет ситуацию.