В С++ можно объявить ссылку, чтобы указать, что ничто никогда не изменит ее?
Если я делаю
typedef void Cb();
int foo(int const& a, Cb cb) {
int x = a;
cb();
return x - a;
}
и скомпилировать с g++ -O3 -save-temps -c foo.cpp
, я вижу, что вычитание сохраняется, а если cb();
закомментировано, вся функция оптимизируется на
xorl %eax, %eax
Есть ли что-то, что я могу сделать для спецификации параметра a
, так что вычитание будет оптимизировано независимо от вызова cb()
и без принуждения a
к уникальной ссылке (то есть, что это может быть указано в другом месте, но что ни одна из этих ссылок не будет изменена)?
Ответы
Ответ 1
Там расширение __restrict
, вы можете попробовать это на gcc.godbolt.org:
typedef void Cb();
int foo(const int & __restrict a, Cb cb) {
int x = a;
cb();
return x - a;
}
Любопытно, что только clang делает оптимизацию, gcc не делает это.
Обратите внимание, что ограничение на сглаживание рассматривается как часть стандарта С++:
Возможно, в будущем вы можете сделать это по стандарту.
Ответ 2
Выполнение предложенной оптимизации было бы неверным, поскольку остальная часть кода могла бы быть:
static int var;
void func()
{
var++;
}
// ...
foo(var, func);
Я не знаю какого-либо атрибута, специфичного для компилятора, который вы можете сказать, что cb()
не будет изменять a
.
Ответ 3
Почему бы вам просто не переместить строку int x = a;
под вызов функции?
Если cb()
не влияет ни на x
, ни на a
, вы должны это делать, и я думаю, что компилятор снова оптимизирует вызов, потому что x не может измениться между двумя вызовами. Если есть причина, по которой вы не можете изменить порядок этих двух вызовов, вы, вероятно, не сможете оптимизировать ее в первую очередь.
Это не то, что вы можете намекать на компилятор, потому что нет способа гарантировать, что ни x, ни a не изменились после вызова cb()
.
Подумайте об этом как о порядке доступа для чтения/записи. Если во время cb()
нет доступа для чтения или записи к a
и x
, вы можете выполнить ручное переупорядочение вызова функции.
Если написано a
или x
, вы не можете изменить порядок, и оптимизация будет неправильной.
Если читать x
, вы не можете переупорядочить вызов функции, но если x
действительно только прочитано, вы можете прочитать вместо a и определить x
только после вызова, так как он будет иметь такое же значение как a
, если бы вы объявили его перед вызовом.
Ответ 4
Вы можете использовать C restrict
в ссылке, если ваш компилятор поддерживает это расширение.
(Некоторые компиляторы разрешают __restrict
или __restrict__
, которые являются частью пространства имен реализаций.)
Это обещание от вас компилятору о том, что объект не сглажен нигде, и он может его оптимизировать.
Если вы солгали компилятору, вы получите поврежденный код, которого вы заслуживаете.
Ответ 5
__ атрибут __ ((const)) достаточно
Как описано в: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Function-Attributes.html, он сообщает компилятору, что данная функция не изменяет глобальные (хотя и может их читать).
Существует также подмножество pure
const
, что также запрещает глобальные чтения.
Рассмотрим следующий упрощенный пример:
int __attribute__((const)) g();
int f(int a) {
int x = a;
g();
return x - a;
}
В g++
4.8 x86_64 -O3
он компилируется в:
-
xor %eax,%eax
с const
- большая функция, которая не допускает, что
a
не изменяется без const
Кажется, что нет более тонкого атрибута, который говорит, что функция не изменяет заданную переменную по мере необходимости.
Может ли __attribute __ ((const)) применяться к указателям на функции?
Попробуйте:
int f(int& a, void __attribute__((const)) (*g)(void)) {
int x = a;
(*g)();
return x - a;
}
Снова компилируется xor %eax,%eax
и большая функция без const
, поэтому ответ да.
Этот синтаксис задавался по адресу: Функциональный указатель на функцию __attribute __ ((const))?
Он также появился в 2011 году в списке рассылки по адресу: http://comments.gmane.org/gmane.comp.gcc.help/38052 В то время, по крайней мере, он работал только для некоторых атрибутов.
Может ли __attribute __ ((const)) быть добавлен в typedef?
Это работает:
typedef void __attribute__((const)) (*g_t)(void);
int f(int& a, g_t g) {
int x = a;
(*g)();
return x - a;
}
или
typedef void (g_t)(void);
int f(int& a, g_t __attribute__((const)) g) {
int x = a;
g();
return x - a;
}
Но я не мог найти способ как поместить атрибут в typedef, так и передать функцию, а не указатель, например:
typedef void __attribute__((const)) (g_t)(void);
GCC дает предупреждение о том, что в этом случае атрибут игнорировался.