Константы и оптимизация компилятора в C++
Я прочитал все советы о правильности констант в C++ и о том, что это важно (частично), потому что это помогает компилятору оптимизировать ваш код. То, что я никогда не видел, является хорошим объяснением того, как компилятор использует эту информацию для оптимизации кода, даже хорошие книги не объясняют, что происходит за кулисами.
Например, как компилятор оптимизирует метод, который объявлен как const, по сравнению с методом, который не является, но должен быть. Что происходит, когда вы вводите изменяемые переменные? Они влияют на эти оптимизации const методов?
12 ответов
Давайте оставим без внимания методы и посмотрим только на const объекты; у компилятора здесь гораздо больше возможностей для оптимизации. Если объект объявлен как const, то (ISO/IEC 14882:2003 7.1.5.1(4)):
За исключением того, что любой член класса, объявленный mutable (7.1.1), может быть изменен, любая попытка изменить объект const в течение его времени жизни (3.8) приводит к неопределенному поведению.
Позволяет игнорировать объекты, которые могут иметь изменяемые члены - компилятор может предположить, что объект не будет изменен, поэтому он может привести к значительной оптимизации. Эти оптимизации могут включать в себя такие вещи, как:
- включение значения объекта непосредственно в коды операций машинных команд
- полное исключение кода, который никогда не будет достигнут, потому что объект const используется в условном выражении, известном во время компиляции
- Развертывание цикла, если объект const контролирует количество итераций цикла.
Обратите внимание, что этот материал применяется только в том случае, если фактический объект является константным - он не применяется к объектам, доступ к которым осуществляется с помощью указателей или ссылок констант, поскольку эти пути доступа могут привести к объектам, которые не являются константными (даже четко определено изменять объекты, хотя константные указатели / ссылки, пока фактический объект не является константным, и вы отбрасываете константу пути доступа к объекту).
На практике я не думаю, что существуют компиляторы, которые выполняют какую-либо значительную оптимизацию для всех видов const-объектов. но для объектов, которые являются примитивными типами (целые числа, символы и т. д.), я думаю, что компиляторы могут быть довольно агрессивными в оптимизации использования этих элементов.
Я думаю, что ключевое слово const было введено в первую очередь для проверки семантики программы, а не для оптимизации.
Херб Саттер, в статье GotW #81, очень хорошо объясняет, почему компилятор не может ничего оптимизировать при передаче параметров по константной ссылке или при объявлении возвращаемого значения const. Причина в том, что компилятор не может быть уверен, что указанный объект не будет изменен, даже если объявлен const: можно использовать const_cast или какой-то другой код может иметь неконстантную ссылку на тот же объект.
Однако, цитируя статью Херба Саттера:
Существует [только] один случай, когда выражение "const" может действительно что-то значить, и это когда объекты становятся const в той точке, в которой они определены. В этом случае компилятор часто может успешно помещать такие "действительно постоянные" объекты в постоянную память [...].
В этой статье намного больше, поэтому я рекомендую вам прочитать ее: после этого вы будете лучше понимать постоянную оптимизацию.
начинается размахивание руками
По существу, чем раньше данные исправлены, тем больше компилятор может перемещаться вокруг фактического назначения данных, гарантируя, что конвейер не остановится
конец руки
Мех. Const-правильность - это больше стиль / проверка ошибок, чем оптимизация. Полноценный оптимизирующий компилятор будет следить за использованием переменной и может определять, является ли переменная эффективной или нет.
Кроме того, компилятор не может полагаться на то, что вы говорите ему правду - вы можете отбрасывать const внутри библиотечной функции, о которой он не знает.
Так что да, правильность const - это достойная вещь, к которой стоит стремиться, но она не говорит компилятору ничего, что она сама не поймет, предполагая хороший оптимизирующий компилятор.
Он не оптимизирует функцию, объявленную как 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() не является const, значение необходимо перезагрузить перед вызовом Flong(). Если someFunc является const, тогда мы можем вызвать Flong со значением, которое все еще находится в регистре.
Основная причина использования методов в качестве const заключается в правильности const, а не в возможной оптимизации компиляции самого метода.
Если переменные являются постоянными, их можно (теоретически) оптимизировать. Но только область видимости видна компилятору. В конце концов, компилятор должен разрешить их модификацию с помощью const_cast в другом месте.
Это все верные ответы, но ответы и вопрос, похоже, предполагают одно: оптимизация компилятора действительно имеет значение.
Существует только один вид кода, где оптимизация компилятора имеет значение, то есть код, который
- тесная внутренняя петля,
- в коде, который вы компилируете, в отличие от сторонней библиотеки,
- не содержит вызовов функций или методов (даже скрытых),
- где счетчик программы тратит заметную долю своего времени
Если остальные 99% кода будут оптимизированы до N-й степени, это не будет иметь большого значения, потому что это имеет значение только в коде, где программный счетчик фактически тратит время (которое вы можете найти путем выборки).
Я был бы удивлен, если бы оптимизатор фактически поместил много акций в объявление const. Существует много кода, который в конечном итоге отбрасывает константу, это был бы очень безрассудный оптимизатор, который полагался на объявление программиста, чтобы предположить, когда состояние может измениться.
const-правильность также полезна в качестве документации. Если функция или параметр указаны как const, мне не нужно беспокоиться об изменении значения из-под моего кода (если только кто-то в команде не очень непослушный). Я не уверен, что это действительно стоило бы того, если бы оно не было встроено в библиотеку.
Наиболее очевидный момент, когда const
Прямая оптимизация заключается в передаче аргументов функции. Часто важно убедиться, что функция не изменяет данные, поэтому единственными реальными вариантами выбора сигнатуры функции являются:
void f(Type dont_modify); // or
void f(Type const& dont_modify);
Конечно, настоящим волшебством здесь является передача ссылки, а не создание (дорогой) копии объекта. Но если ссылка не была помечена как const
, это ослабило бы семантику этой функции и имело бы негативные последствия (например, усложнение отслеживания ошибок). Следовательно, const
позволяет оптимизировать здесь.
/ EDIT: на самом деле, хороший компилятор может проанализировать поток управления функции, определить, что он не изменяет аргумент, и выполнить оптимизацию (передавая ссылку, а не копию). const
Вот только помощь для компилятора. Однако, поскольку C++ имеет довольно сложную семантику и такой анализ потока управления может быть очень дорогим для больших функций, мы, вероятно, не должны полагаться на компиляторы для этого. У кого-нибудь есть какие-либо данные, чтобы поддержать меня / доказать, что я не прав?
/ EDIT2: и да, как только в игру вступают пользовательские конструкторы копирования, это становится еще сложнее, потому что, к сожалению, компиляторам не разрешается вызывать их в этой ситуации.
Этот код,
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! = оптимизация
Фактически, это допустимый модификатор C++:
volatile const
const
помогает компиляторам оптимизироваться главным образом потому, что он позволяет писать оптимизируемый код. Если вы не добавите const_cast
,