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



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



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

681   7  

7 ответов:

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

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

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

только const может разрешить оптимизацию в таких случаях:

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

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

чтобы ответить на ваш вопрос" side note", (a) указатель const является указателем; и (b) указатель const может равняться 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), за исключением расширения, специфичного для компилятора.

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

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

здесь компилятор знает для печати 42 без необходимости исследовать тело bar() (который может быть не виден в curent translation unit), потому что все изменения в foo являются незаконными (это то же самое, что Немо пример).

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

extern void bar(const int *restrict p);

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

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

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

  • const_cast
  • mutable

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.