Соответствует ли const-correctness компилятору больше возможностей для оптимизации?

Я знаю, что он улучшает читаемость и делает программу менее подверженной ошибкам, но насколько она улучшает производительность?

И на стороне примечания, какое основное различие между ссылкой и указателем const? Я бы предположил, что они хранятся в памяти по-разному, но как это?

Спасибо.

Ответ 1

[Edit: OK, поэтому этот вопрос более тонкий, чем я думал вначале.]

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

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

Пример:

int foo(const int *p) {
    int x = *p;
    bar(x);
    x = *p;
    return x;
}

Компилятор не может предположить, что *p не изменен вызовом bar(), поскольку p может быть (например) указателем на глобальный int и bar() может его изменить.

Если компилятор знает достаточно о вызывающем элементе foo(), а содержимое bar(), которое может доказать, что bar() не изменяет *p, тогда оно также может выполнить это доказательство без объявления const.

Но это верно в целом. Поскольку const влияет только на область применения декларации, компилятор уже может видеть, как вы обрабатываете указатель или ссылку в этой области; он уже знает, что вы не изменяете базовый объект.

Короче говоря, все const в этом контексте не позволяют вам ошибаться. Он не сообщает компилятору ничего, чего он еще не знает, и поэтому он не имеет отношения к оптимизации.

Как насчет функций, вызывающих foo()? Как:

int x = 37;
foo(&x);
printf("%d\n", x);

Может ли компилятор доказать, что это печатает 37, так как foo() принимает const int *?

Нет. Даже если foo() принимает указатель-на-const, он может отбросить const-ness и изменить int. (Это не поведение undefined.) Здесь опять же компилятор вообще не может делать какие-либо предположения; и если он достаточно знает о foo(), чтобы сделать такую ​​оптимизацию, он будет знать, что даже без const.

Единственное время const может позволить оптимизацию - это такие случаи:

const int x = 37;
foo(&x);
printf("%d\n", x);

Здесь, чтобы изменить x на любой механизм (например, сделав указатель на него и отбросив const), вызывается undefined Поведение. Поэтому компилятор может предположить, что вы этого не делаете, и он может распространять константу 37 в printf(). Такая оптимизация легальна для любого объекта, который вы объявляете const. (На практике локальная переменная, к которой вы никогда не примете ссылку, не принесет пользы, потому что компилятор уже может увидеть, изменяете ли вы ее в своей области.)

Чтобы ответить на вопрос "side note", (a) указатель const является указателем; и (b) указатель const может равняться NULL. Вы правы, что внутреннее представление (т.е. Адрес), скорее всего, одно и то же.

[обновление]

Как Christoph указывает в комментариях, мой ответ неполный, потому что он не упоминает restrict.

В разделе 6.7.3.1 (4) стандарта C99 говорится:

Во время каждого исполнения B пусть L - любое l-значение, которое имеет & L на основе P. Если L используется для доступ к значению объекта X, который он обозначает, и X также модифицируется (любым способом),  то применяются следующие требования: T не должен быть const-квалифицированным....

(Здесь B является базовым блоком, над которым P, ограничитель-указатель-to-T, находится в области.)

Итак, если функция C foo() объявлена ​​следующим образом:

foo(const int * restrict p)

... тогда компилятор может предположить, что никакие изменения в *p не происходят во время жизни p - то есть во время выполнения foo() - потому что в противном случае Поведение будет Undefined.

Итак, в принципе, объединение restrict с указателем на константу может позволить обе оптимизации, которые были уволены выше. Интересно, действительно ли какие-либо компиляторы действительно реализуют такую ​​оптимизацию? (GCC 4.5.2, по крайней мере, нет.)

Обратите внимание, что restrict существует только в C, а не С++ (даже С++ 0x), кроме как в качестве расширения для компилятора.

Ответ 2

В верхней части моей головы я могу думать о двух случаях, когда правильная const -qualification допускает дополнительные оптимизации (в тех случаях, когда анализ цельной программы недоступен):

const int foo = 42;
bar(&foo);
printf("%i", foo);

Здесь компилятор знает, чтобы печатать 42, не проверяя тело bar() (которое может не отображаться в блоке курсирования), поскольку все изменения в foo являются незаконными (это то же самое, что и Пример Nemo).

Однако это также возможно без маркировки foo как const, объявив bar() как

extern void bar(const int *restrict p);

Во многих случаях программист на самом деле хочет restrict -qualified pointers-to-const, а не простые указатели-to-const как параметры функции, так как только первые предоставляют какие-либо гарантии относительно изменчивости заостренных -объектам.

Что касается второй части вашего вопроса: для всех практических целей ссылку С++ можно рассматривать как константный указатель (а не указатель на постоянное значение!) с автоматической косвенностью - это не "безопаснее" или "быстрее", чем указатель, просто удобнее.

Ответ 3

В С++ есть две проблемы с const (что касается оптимизации):

  • const_cast
  • mutable

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

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

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

Ответ 4

const-correctness обычно не помогает производительности; большинство компиляторов даже не потрудились отслеживать консистенцию за пределами интерфейса. Значения переменных как const могут помочь, в зависимости от ситуации.

Ссылки и указатели хранятся точно так же в памяти.

Ответ 5

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

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

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

Ответ 6

Одно дело, если вы объявите глобальную переменную const, ее можно будет поместить в часть только для чтения библиотеки или исполняемого файла и, таким образом, поделиться ею между несколькими процессами с помощью mmap только для чтения. Это может быть большой победой в Linux на Linux, если у вас много данных, объявленных в глобальных переменных.

Другая ситуация, если вы объявляете постоянное глобальное целое число или float или enum, компилятор может просто поместить константу inline, а не использовать ссылку на переменную. Это немного быстрее, я верю, хотя я не специалист по компилятору.

Ссылки - это всего лишь указатели внизу, реалистичные.

Ответ 7

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

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

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

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