Как заставить GCC поместить константы в память, а не генерировать их? - PullRequest
15 голосов
/ 16 февраля 2011

У меня есть много константных массивов, определенных в нескольких функциях.Примерно так:

const float values[4] = {-4312.435f,  -432.44333f,  4.798, 7898.89};

После проверки вывода ассемблера gcc я заметил, что эти константы генерируются при каждом запуске функций.Это довольно неэффективно.Я подозреваю, что это потому, что спецификация C / C ++ говорит, что даже если данные const, компилятор не может предположить, что они не будут изменены (например, через const_cast).Можно ли заставить gcc думать иначе?

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

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

К сожалению, даже когда константы определены static, они регенерируютсякаждый пробег.Я использую -O3, если это помогает.

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

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

EDIT3

Проблема была в моем тестовом примере, где я определил двамассивы поблизости, но один из них был предназначен для генерации.Затем ассемблер ввел меня в заблуждение.Извините еще раз и спасибо!

Ответы [ 5 ]

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

Объявите их с ключевым словом static.

Редактировать: отвечая на ваш комментарий, я могу показать вам код:

Это ожидаемое поведение. Вы делаете или видите что-то другое?

$ cat foo.c++
int main(void)
{
    static const float foos[] = {1.234f, 5.678f, 9.012f};
    return 0;
}
$ g++ -S foo.c++
$ cat foo.s
    .file   "foo.c++"
    .text
.globl main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    pushq   %rbp
    .cfi_def_cfa_offset 16
    movq    %rsp, %rbp
    .cfi_offset 6, -16
    .cfi_def_cfa_register 6
    movl    $0, %eax
    leave
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .section    .rodata
    .align 4
    .type   _ZZ4mainE4foos, @object
    .size   _ZZ4mainE4foos, 12
_ZZ4mainE4foos:
    .long   1067316150
    .long   1085649453
    .long   1091580199
    .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
    .section    .note.GNU-stack,"",@progbits
11 голосов
/ 16 февраля 2011

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

Поэтому используйте функцию языка, которая означает, что объявление ссылается на одно и то же при каждом вызове функции. То есть функциональная переменная static:

static const float values[4] = {-4312.435f, -432.44333f, 4.798, 7898.89};
5 голосов
/ 16 февраля 2011

Измените определение на

static const float values[4] = {-4312.435f,  -432.44333f,  4.798, 7898.89};

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

Вы также можете попытаться переместитьэтот массив вне функции (сделайте его глобальным массивом с некоторым префиксом, таким как function1_values).

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

Если вы считаете "flds" или "movss" инстанции как восстанавливающие -не.Константы будут храниться в разделе .rodata файла elf, но для их использования компилятор должен загрузить их в регистры.Таким образом, fld & movss будет загружать константу только из памяти, и невозможно получить значение из памяти без ее загрузки.

Пример кода:

int function4(float *a, int sz)
{
    int i;
const float values[4] = {-4312.435f,  -432.44333f,  4.798, 7898.89};
    for(i=4;i<sz;i++);
        a[i]+=a[i-1]*values[0]+a[i-2]*values[1]+a[i-3]*values[2]+a[i-4]*values[3];
    return i;
}

gcc-4.5.2 -O3 ac -fverbose-asm -mfpmath = sse -march = native -S

Ассемблер для тела цикла:

.L2:
    movl    -20(%ebp), %ecx # %sfp, D.2677
    leal    (%edx,%ecx), %ecx       #, D.2677  
    movss   .LC0, %xmm0     #, tmp192     << THIS is a constant loading
    mulss   (%edx,%edi), %xmm0      #* prephitmp.46, tmp192
    movss   .LC1, %xmm1     #, tmp179
    mulss   (%edx,%esi), %xmm1      #* prephitmp.46, tmp179
    addss   %xmm1, %xmm0    # tmp179, tmp192
    movss   .LC2, %xmm1     #, tmp183
    mulss   (%edx,%ebx), %xmm1      #* prephitmp.46, tmp183
    addss   %xmm1, %xmm0    # tmp183, tmp192
    movss   .LC3, %xmm1     #, tmp187
    movl    -16(%ebp), %ebx # %sfp,

А константы хранятся в .rodata:

    .section        .rodata.cst4,"aM",@progbits,4
    .align 4
.LC0:
    .long   -981023877
    .align 4
.LC1:
    .long   -1009239873
    .align 4
.LC2:
    .long   1083803959
    .align 4
.LC3:
    .long   1173804831
1 голос
/ 16 февраля 2011

Разместите все свои

const float values[4] = {-4312.435f,  -432.44333f,  4.798, 7898.89};

в отдельном файле .cpp.

Объявите их extern в файле, который использует эти массивы

extern const float values[4];

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

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

Я не понимаю, где вы видите проблему, компилятор делает разумную вещь, оптимизируя константы.Вы можете предотвратить это, потребовав внешнюю связь:

extern const float values[4];
...