Атрибут const gcc и глобальные переменные const

Из документов GCC:

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

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

Например:

int const ConstantModulusValue = 3;

int foo(int value) {
    return foo % ConstantModulusValue;
}

Использование ConstantModulusValueНасколько я понимаю, это означает, что эта функция не должна быть отмечена const что, опять же, мне кажется странным. Есть ли какая-то опасность в маркировке этого const что я не вижу.?

1 ответ

Решение

Эти атрибуты позволяют компилятору знать, безопасно ли пропускать вызовы функции, не зная, как она реализована.

чистый атрибут в основном говорит, что результат функции зависит только от аргументов функции и глобального состояния; кроме того, сама функция не изменяет глобальное состояние.

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

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

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

В качестве примера рассмотрим эту программу:

int foo(int) __attribute__((pure));
int bar(int) __attribute__((const));
void unknown();

int test1(int a)
{
    int x = foo(a);
    int y = foo(a);
    return x + y;
}

int test2(int a)
{
    int x = bar(a);
    int y = bar(a);
    return x + y;
}

int test3(int a)
{
    int x = foo(a);
    unknown();
    int y = foo(a);
    return x + y;
}

int test4(int a)
{
    int x = bar(a);
    unknown();
    int y = bar(a);
    return x + y;
}

Компиляция с помощью gcc 4.8.1 и анализ сборки показывают, что test1() и test2() оба вызывают соответствующую функцию только один раз, а затем умножают результат на 2; test3() выполняет 3 вызова - 2 вызова foo и 1 вызов неизвестного; test4() выполняет вызов bar(), за которым следует вызов unknown () и возвращает результат bar(), умноженный на 2.

Это поведение соответствует объяснению выше - unknown() может изменять глобальное состояние, поэтому компилятор не может исключить дополнительные вызовы foo(), но может исключить дополнительные вызовы bar().

Другие вопросы по тегам