Константы и оптимизация компилятора в C++

Я прочитал все советы по константной корректности в C++ и о том, что это важно (отчасти), потому что помогает компилятору оптимизировать ваш код. Чего я никогда не видел, так это хорошего объяснения того, как компилятор использует эту информацию для оптимизации кода, даже в хороших книгах не объясняется, что происходит за кулисами.

Например, как компилятор оптимизирует метод, который объявлен как const, против метода, который не является, но должен быть. Что происходит, когда вы вводите изменяемые переменные? Влияют ли они на оптимизацию константных методов?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
49
0
20 494
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

махание рукой начинается

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

конец махать рукой

Он не оптимизирует функцию, объявленную const.

Он может оптимизировать функции, которые вызов объявлены как const.

void someType::somefunc();

void MyFunc()
{
    someType A(4);   // 
    Fling(A.m_val);
    A.someFunc();
    Flong(A.m_val);
}

Здесь для вызова Fling в регистр ЦП необходимо загрузить valud A.m_val. Если someFunc () не является константой, значение необходимо будет перезагрузить перед вызовом Flong (). Если someFunc имеет значение const, мы можем вызвать Flong со значением, которое все еще находится в регистре.

Я не думаю, что const вообще помогает в оптимизации. A.someFunc () может легко выполнить const_cast <someType *> (this) -> m_val = 42; или любое количество других вещей, которые могут изменить m_val (например, если в глобальной переменной есть неконстантный указатель на A, someFunc может его изменить)

Qwertie 17.10.2008 19:14

Компилятору разрешено предположить, что вы не откажетесь от const; вы делаете это на свой страх и риск. И поскольку A полностью находится внутри MyFunc (), компилятор может отслеживать, есть ли на него глобальный указатель. (В этом примере не может быть)

James Curran 17.10.2008 19:17

Я не уверен, что этот ответ (и комментарий) правильный. Насколько мне известно, полностью законно отбрасывать константность и изменять объект, если это не константный объект. В вашем примере A не является константой, поэтому somefunc (который не объявлен как const, btw) разрешено отбрасывать константу на this и изменять текущий объект. Однако, если A был определен как const someType A(4), то его изменение путем отказа от константности привело бы к неопределенному поведению. Следовательно, я не думаю, что компилятор может что-либо оптимизировать в приведенном вами примере.

Luc Touraille 12.03.2011 00:01

Важно различать константный объект и константный дескриптор (указатель или ссылка) на объект (который может быть константным или нет). Первое потенциально может привести к оптимизации компилятора, второе - нет (AFAIK).

Luc Touraille 12.03.2011 00:03

@JamesCurran "Компилятору разрешено предположить, что вы не откажетесь от const" Неправильно.

curiousguy 17.12.2011 02:53

@curiousguy: Хотели бы вы поддержать это утверждение? Если компилятор не может предположить, что константный объект остается константным, ключевое слово становится бессмысленным. (согласно Стандарту 7.1.6.1 (4) - отказ от константности приводит к неопределенному поведению)

James Curran 26.12.2011 18:51

@JamesCurran Вы не можете изменить объект const, но можете отбросить const. Если вы решите отказаться от const, вы несете ответственность за то, чтобы в конечном итоге вы не изменили объект const.

curiousguy 26.12.2011 19:49

Было бы действительно интересно увидеть ассемблер, созданный для двух версий, где someFunce объявлен с \ без const.

Anton Daneyko 09.01.2013 23:12

Основная причина использования методы в качестве константы - это правильность константы, а не возможная оптимизация компиляции самого метода.

Если переменные являются постоянными, их можно (теоретически) оптимизировать. Но компилятор видит только область видимости. В конце концов, компилятор должен разрешить их изменение с помощью const_cast в другом месте.

Мех. Постоянная корректность - это скорее проверка стиля / ошибок, чем оптимизация. Полноценный оптимизирующий компилятор будет следить за использованием переменных и может определять, является ли переменная фактически константной или нет.

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

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

Я был бы удивлен, если бы оптимизатор действительно вложил много средств в объявление const. Существует много кода, который в конечном итоге отбрасывает константу, это был бы очень безрассудный оптимизатор, который полагался на объявление программиста, чтобы предположить, когда состояние может измениться.

Отказаться от константности было бы очень легкомысленным программистом. Что бы она подумала: «Неаа, документация на самом деле не означает, что она неизменна. Это не относится к меня»?

gnud 17.10.2008 18:30

@gnud: Неактуально - компилятор не может предположить, что программист этого не делает.

Mike F 17.10.2008 18:36

@Mike F: Очень важно, компилятор МОЖЕТ предполагать, что программист этого не делает. Стандарт утверждает (7.1.6.1/4), что: «За исключением того, что любой член класса, объявленный изменяемым, может быть изменен, любая попытка изменить константный объект в течение его времени жизни приводит к неопределенному поведению».

Nick Lewis 23.07.2009 20:39

@NickLewis Конечно, но я предполагаю, что в ответе говорилось о ссылках / указателях const на объекты, не объявленные const, и о возможности отбрасывать const на них и, таким образом, изменять референт, который определен является и который, наряду с mutable и отбрасыванием const на экземпляре, отличном от const, является очень веским возражением против часто повторяемого, но никогда не цитируемого популярного мифа о том, что const дает некоторую свободу действий оптимизирующим компиляторам.

underscore_d 16.07.2016 15:18

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

Херб Саттер в Статья GotW # 81 очень хорошо объясняет, почему компилятор не может ничего оптимизировать при передаче параметров по ссылке const или при объявлении возвращаемого значения const. Причина в том, что компилятор не может быть уверен, что объект, на который ссылается, не будет изменен, даже если он объявлен как const: можно использовать const_cast, или какой-либо другой код может иметь неконстантную ссылку на тот же объект.

Однако, цитируя статью Херба Саттера:

There is [only] one case where saying "const" can really mean something, and that is when objects are made const at the point they are defined. In that case, the compiler can often successfully put such "really const" objects into read-only memory[...].

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

Наиболее очевидный момент, когда const является прямой оптимизацией, - это передача аргументов функции. Часто важно убедиться, что функция не изменяет данные, поэтому единственными реальными вариантами подписи функции являются следующие:

void f(Type dont_modify); // or
void f(Type const& dont_modify);

Конечно, настоящая магия здесь заключается в передаче ссылки, а не в создании (дорогой) копии объекта. Но если бы ссылка не была помечена как const, это ослабило бы семантику этой функции и имело бы отрицательные последствия (например, усложнило бы отслеживание ошибок). Следовательно, const позволяет здесь оптимизацию.

/ EDIT: на самом деле хороший компилятор может анализировать поток управления функцией, определять, что он не изменяет аргумент, и сам выполнять оптимизацию (передавая ссылку, а не копию). const здесь просто помощник для компилятора. Однако, поскольку C++ имеет довольно сложную семантику и такой анализ потока управления может быть очень дорогостоящим для больших функций, нам, вероятно, не следует полагаться в этом на компиляторы. Есть ли у кого-нибудь данные, подтверждающие мою неправоту?

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

Либо ваши инструменты сборки нуждаются в оптимизации времени компоновки, либо ваш конструктор копирования (и, возможно, деструктор) должен быть видимым для компилятора на сайте вызова. В противном случае компилятор не сможет оптимизировать копию.

Tom 24.12.2008 03:49

Этот код,

class Test
{
public:
  Test (int value) : m_value (value)
  {
  }

  void SetValue (int value) const
  {
    const_cast <Test&>(*this).MySetValue (value);
  }

  int Value () const
  {
    return m_value;
  }

private:
  void MySetValue (int value)
  {
    m_value = value;
  }

  int
    m_value;
};

void modify (const Test &test, int value) 
{
  test.SetValue (value);
}

void main ()
{
  const Test
    test (100);

  cout << test.Value () << endl;
  modify (test, 50);
  cout << test.Value () << endl;
}

выходы:

100
50

что означает, что объявленный объект const был изменен в функции-члене const. Наличие const_cast (и ключевого слова mutable) в языке C++ означает, что ключевое слово const не может помочь компилятору в создании оптимизированного кода. И, как я отмечал в своих предыдущих постах, это может даже дать неожиданные результаты.

Как общее правило:

const != optimisation

Фактически, это законный модификатор C++:

volatile const

Вы не понимаете const.

curiousguy 17.12.2011 02:51

Хотя я согласен с тем, что оптимизация часто рекламируется, но никогда не демонстрировала того, что я видел, этот пост не свидетельствует в пользу этого. вы намеренно бомбардируете компилятор требованиями, чтобы он позволил вам выстрелить себе в ногу, и в процессе вызываете UB, а затем делаете вывод, что const неисправен. нет, ваш код не работает. void main - прекрасная вишенка на вершине этого доказательства.

underscore_d 16.07.2016 00:27
Ответ принят как подходящий

Не обращаем внимания на методы и будем смотреть только на константные объекты; здесь у компилятора гораздо больше возможностей для оптимизации. Если объект объявлен как const, то (ISO / IEC 14882: 2003 7.1.5.1 (4)):

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

Не обращаем внимания на объекты, которые могут иметь изменяемые члены - компилятор может предположить, что объект не будет изменен, поэтому он может произвести значительную оптимизацию. Эти оптимизации могут включать в себя такие вещи, как:

  • включение значения объекта непосредственно в коды операций машинных команд
  • полное исключение кода, который никогда не может быть достигнут, потому что объект const используется в условном выражении, известном во время компиляции
  • разворачивание цикла, если объект const контролирует количество итераций цикла

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

На практике я не думаю, что существуют компиляторы, которые выполняют какую-либо значительную оптимизацию для всех видов константных объектов. но для объектов, которые являются примитивными типами (целые числа, символы и т. д.), я думаю, что компиляторы могут быть довольно агрессивными в оптимизации использование этих предметов.

Означает ли это, что const_cast запрещает удалять константность для чего-либо, кроме примитивных типов или константных объектов с изменяемыми членами?

Andriy Tylychko 25.03.2013 16:03

@AndyT: Да, это так. Вам разрешено const_cast убрать константу только для объектов, которые изначально не являются константами.

Alexandre C. 30.10.2013 11:59

@AlexandreC. нет, вы не можете const_cast убрать постоянство любого объекта. Но попытка изменить это поведение не определено. Сам гипс всегда в порядке.

Simple 30.10.2013 14:08

@Simple: Технически вы правы. Однако в чем смысл const_casting, если вы ничего не делаете с объектом? :)

Alexandre C. 31.10.2013 00:43

@AlexandreC. полезно вызвать перегрузку const из перегрузки, отличной от const, а затем отбросить постоянство результата.

Simple 31.10.2013 13:37

@Simple: Да, я знаю эту технику. Вы же предлагаете результат на доработку.

Alexandre C. 01.11.2013 00:23

@AlexandreC .: const_cast можно использовать только при наличии потенциал модификации, в некоторых случаях, не создавая возможности сразу выполнять такие модификации. Например, вы можете захотеть, чтобы тип const_iterator внутренне хранил (константный) iterator, только для того, чтобы иметь возможность возвращать его, когда неконстантный контейнер вызывает свой метод erase с этим const_iterator (так как это то, что метод должен возвращаться). Любое прямое использование const_iterator защитит внутреннюю неконстантность и не позволит изменить цель.

Marc van Leeuwen 26.09.2014 18:20

Все возможности оптимизации, упомянутые в этом ответе, кажутся возможными только в случае констант время компиляции. Они кажутся бесполезными в случае объявленных объектов const, значение которых не может быть известно компилятору, например, потому что их конструктор использует непостоянные аргументы.

Marc van Leeuwen 26.09.2014 18:23

@MarcvanLeeuwen: Я хочу сказать, что переменные, на которые ссылается внутренний изменяемый итератор, изначально не являются константами. В этом случае да, вы можете использовать const_cast.

Alexandre C. 26.09.2014 23:16

С каких это пор "я, компиляторы считать, могу бла-бла" ответ? Многие люди считатьconst обеспечивают некоторую выгоду для оптимизации и громко заявляют об этом повсюду, но, кажется, никогда не представляют сгенерированный код, демонстрирующий это ... Несмотря на это, новички все равно им верят, и миф раздувается навсегда. Вздох.

underscore_d 16.07.2016 00:32

@AlexandreC. возможно, я хочу вызвать старую функцию C, которая принимает char * (потому что const не было, когда код был написан), и я знаю, что функция фактически не изменяет переданный указатель. Тогда я мог бы const_cast убрать константность и передать результат в функцию.

James Picone 09.01.2018 09:36

@ Marc van Leeuwen: если вычисление «x = f (y)» появляется в цикле, а «y» инвариантно по отношению к циклу, а вычисление «f» не имеет побочных эффектов, это вычисление может быть исключено из цикла . Это возможность оптимизации, которая не является константой времени компиляции, и компиляторы делают это все время. Поэтому было бы неплохо спросить, можно ли это поднять с помощью константной функции-члена "f". И я думаю, что нет, потому что компилятор не всегда может увидеть, влияет ли «f» на изменяемые элементы данных.

Victor Eijkhout 11.10.2019 00:45

OP должен серьезно рассмотреть вопрос об удалении статуса "принято" для этого ответа. По словам Чендлера Каррута (который «возглавляет группы C++, Clang и LLVM в Google»), практически не существует оптимизации, которая могла бы иметь место для чего-либо const. Он упомянул, что есть, возможно, «два случая» оптимизации может, но я не знаю, что это такое. Поэтому люди, которые собственно писать оптимизаторы для компиляторов не оптимизируют для const, потому что его можно просто выбросить. Это соответствует тому, что они на самом деле говорят и делают, а не тому, что мы думаем / желаем об этом.

jdk1.0 10.09.2020 16:41

const помогает компиляторам оптимизировать в основном потому, что позволяет писать оптимизируемый код. Разве что кинуть const_cast.

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

Tom 24.12.2008 03:53

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

Есть только один вид кода, в котором имеет значение оптимизация компилятора, это код, который

  • плотная внутренняя петля,
  • в коде, который вы компилируете, в отличие от сторонней библиотеки,
  • не содержащие вызовов функций или методов (даже скрытых),
  • где счетчик программ тратит заметную часть своего времени

Если остальные 99% кода оптимизированы до N-й степени, это не будет иметь большого значения, потому что это имеет значение только в коде, где счетчик программы фактически тратит время (что вы можете найти путем выборки).

Константа-правильность также полезна как документация. Если функция или параметр указан как const, мне не нужно беспокоиться о том, что значение изменится из-под моего кода (если только кто-то другой в команде не очень капризничает). Я не уверен, что оно того стоило бы, если бы оно не было встроено в библиотеку.

Другие вопросы по теме