如何强制GCC将常量放入内存而不是生成它们?

时间:2011-02-16 19:52:48

标签: c++ c gcc assembly constants

我有几个函数中定义的常量数组 lot 。如下所示:

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

在检查gcc汇编程序输出后,我注意到这些常量是在每次运行函数时生成的。那是非常低效的。我怀疑这是因为C / C ++规范说即使数据是const,编译器也不能假设它不会被修改(例如通过const_cast)。是否有可能强制gcc不这么认为?

我想在函数体内定义这些常量,因为它们非常复杂。将常数保持在他们使用的位置附近有助于可维护性。

修改

不幸的是,即使定义常量static,它们也会在每次运行时重新生成。如果有帮助,我会使用-O3。

EDIT2

好的,对于第一次编辑感到抱歉,我需要进一步调查。似乎特定的设置以前在某种程度上不允许gcc初始化常量而不重新生成它们。

EDIT3

问题出在我的测试用例中,我在附近定义了两个数组,但其中一个数组是为了生成的。然后汇编程序误导了我。再次抱歉&谢谢!

5 个答案:

答案 0 :(得分:12)

使用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

答案 1 :(得分:11)

编译器实际上可以假定定义为const的值永远不会改变。 (通过const变量访问的内容是另一个故事;我只讨论定义可见且具有const的情况。)这里得到的问题是标准说如果你递归调用你的函数,values的地址每次都会不同。

因此,使用语言功能意味着每次调用函数时声明都引用相同的内容。也就是说,static函数变量:

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

答案 2 :(得分:5)

将定义更改为

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

静态数组不会放在函数堆栈中,因此不会为每个函数调用重新生成它们。

你也可以尝试将这个数组从函数移到外面(使它成为一个带有function1_values之类前缀的全局数组。

编辑:

如果您将"flds""movss"指令视为重新生成 - 则不是。常量将存储在elf文件的.rodata部分中,但要使用它们,编译器必须将它们加载到寄存器中。所以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

用于循环体的Assembelr:

.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

答案 3 :(得分:1)

放置所有

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

在单独的.cpp文件中。

在使用这些数组的文件中将它们声明为extern

extern const float values[4];

尽管如评论中所述,对此进行了概述(在真实的应用程序中,这些数组可能会从l1 / l2缓存中抛出)。出于非直观原因,这可能会降低性能。

答案 4 :(得分:0)

我不明白你在哪里看到问题,编译器做了一个合理的事情来优化掉常量。您可以通过要求外部链接来防止这种情况:

extern const float values[4];