Постоянное объединение в оптимизирующих компиляторах - PullRequest
8 голосов
/ 16 февраля 2011

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

1) Определите их в отдельном исходном файле, который позже будет связан с приложением.

2) Определить постоянные на месте.

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

Вопрос:

Будет ли компилятор объединять этиравные постоянные?В частности, какой из следующих методов будет использоваться?

1) Объединение одинаковых констант в модуле компиляции.
2) Объединение одинаковых констант в модуле компоновки (целая программа или библиотека)
3) Объединение констант с любыми данными статических констант, которые имеют одинаковую битовую комбинацию и удовлетворяют требованиям выравнивания в модуле компиляции или во всей программе.

Я использую современный компилятор (GCC4.5).

Я не эксперт в ассемблере, поэтому я не мог ответить на этот вопрос сам, используя несколько простых тестов:)

РЕДАКТИРОВАТЬ:

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

EDIT2:

ПРИМЕР кода

Этот использует постоянную на месте:

float_4 sign(float_4 a)
{
    const __attribute__((aligned(16))) float mask[4] = { //I use a macro for this line
        0x80000000, 0x80000000, 0x80000000, 0x80000000};
    const int128 mask = load(mask);
    return b_and(a, mask);
}

Ответы [ 3 ]

8 голосов
/ 16 февраля 2011

В соответствии с GCC следующая опция делает то, что вам нужно:

-fmerge-constants

Попытка объединить идентичные константы (строковые и плавающиеточечные константы) через единицы компиляции.Эта опция используется по умолчанию для оптимизированной компиляции, если ассемблер и компоновщик ее поддерживают.Используйте -fno-merge-constants, чтобы запретить это поведение.
Включено на уровнях -O, -O2, -O3, -Os.

2 голосов
/ 16 февраля 2011

Если вы определяете константы в вашем заголовочном файле следующим образом:

int const TEN = 10;
// or
enum { ELEVEN = 11 };

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

[max@truth test]$ cat test.cc
int const TEN = 10; // definition available
extern int const TWELVE; // only declaration

int foo(int x) { return x + TEN; }
int bar(int x) { return x + TWELVE; }

[max@truth test]$ g++ -S -o - test.cc | c++filt | egrep -v " *\."
foo(int):
    pushq   %rbp
    movq    %rsp, %rbp
    movl    %edi, -4(%rbp)
    movl    -4(%rbp), %eax
    addl    $10, %eax
    leave
    ret
bar(int):
    pushq   %rbp
    movq    %rsp, %rbp
    movl    %edi, -4(%rbp)
    movl    TWELVE(%rip), %eax
    addl    -4(%rbp), %eax
    leave
    ret
TEN:

Обратите внимание, что в foo(int) это происходит как addl $10, %eax, т. Е. Константа TEN заменяется ее значением. С другой стороны, в bar(int) сначала выполняется movl TWELVE(%rip), %eax для загрузки значения TWELVE из памяти в регистр eax (адрес будет разрешен компоновщиком), а затем выполняется добавление addl -4(%rbp), %eax.

Оптимизированная версия выглядит так:

[max@truth test]$ g++ -O3 -S -o - test.cc | c++filt | egrep -v " *\."
foo(int):
    leal    10(%rdi), %eax
    ret
bar(int):
    movl    TWELVE(%rip), %eax
    addl    %edi, %eax
    ret
0 голосов
/ 16 февраля 2011

Я не думаю, что есть общие ответы на ваши вопросы.Я даю один только для C, правила для C ++ разные.

Это во многом зависит от типов ваших констант.Важным классом являются «целочисленные константные выражения».Их можно определить во время компиляции и, в частности, использовать в качестве значений «целочисленных констант перечисления».Используйте это всякий раз, когда вы можете

enum { myFavoriteDimension = 55/2 };

Для таких констант обычно должно происходить самое лучшее: они реализуются как ассемблеры сразу.У них даже нет места хранения, они напрямую записываются на ассемблер, и ваши вопросы даже не имеют смысла.

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

register double const something = 5.7;

может иметь тот же эффект, что и выше.

Для составных типов (struct, union, массивы) естьнет общего ответа или метода.Я уже видел, что gcc может полностью оптимизировать небольшие массивы (10 элементов или около того).

...