Предоставляет ли const-правильность компилятору больше возможностей для оптимизации? - PullRequest
76 голосов
/ 11 июня 2011

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

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

Ответы [ 7 ]

69 голосов
/ 11 июня 2011

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

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

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

Пример:

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, он может отбросить константу и изменить int. (Это , а не неопределенное поведение.) Здесь снова, компилятор не может делать какие-либо предположения вообще; и если он знает достаточно о foo(), чтобы сделать такую ​​оптимизацию, он будет знать, что даже без const.

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

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

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

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

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

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

Раздел 6.7.3.1 (4) стандарта C99 гласит:

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

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

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

foo(const int * restrict p)

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

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

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

6 голосов
/ 11 июня 2011

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

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

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

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

extern void bar(const int *restrict p);

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

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

5 голосов
/ 11 июня 2011

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

  • const_cast
  • mutable

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

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

4 голосов
/ 11 июня 2011

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

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

2 голосов
/ 11 июня 2011

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

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

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

1 голос
/ 11 июня 2011

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

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

Ссылки - это просто указатели внизу, с точки зрения реализации.

0 голосов
/ 11 июня 2011

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

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

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

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

...