Как заставить GCC помещать константы в память вместо их генерации?

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

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

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

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

ИЗМЕНИТЬ

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

EDIT2

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

EDIT3

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

Ответы

Ответ 1

Объявите их с помощью ключевого слова 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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

EDIT:

Если вы считаете экземпляры "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 a.c -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

Ответ 4

Поместите все свои

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

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

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

extern const float values[4];

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

Ответ 5

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

extern const float values[4];