const char 编译器优化

Kar*_*run 3 c compiler-optimization string-interning

我在两个不同的文件中有全局常量字符定义:

f1:

const char foo1[] = "SAME_VALUE";
Run Code Online (Sandbox Code Playgroud)

f2:

const char foo2[] = "SAME_VALUE";
Run Code Online (Sandbox Code Playgroud)

想了解在最终的二进制文件中是否会对其进行优化以占用内存中的公共空间。这是在海湾合作委员会的背景下

Rac*_* K. 5

这种优化称为字符串实习

GCC-fmerge-constants默认设置标志:

尝试跨编译单元合并相同的常量(字符串常量和浮点常量)。
如果汇编器和链接器支持,则此选项是优化编译的默认选项。使用 -fno-merge-constants 来抑制这种行为。
在 -O、-O2、-O3、-Os 级别启用。

让我们使用名为fc的第三个文件创建一个可执行文件来引用字符串:

#include <stdio.h>

// For proposition#1
extern const char foo1[], foo2[];

// For proposition#2
//extern const char *foo1, *foo2;

int main(void) {

  printf("%s\n", foo1);
  printf("%s\n", foo2);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

当您分别在f1.cf2.c 中定义以下内容时命题#1):

const char foo1[] = "SAME_VALUE";
Run Code Online (Sandbox Code Playgroud)
const char foo2[] = "SAME_VALUE";
Run Code Online (Sandbox Code Playgroud)

这会产生 2 个不同的存储空间,其中存储了字符串“SAME_VALUE”。因此,该字符串是重复的:

$ gcc f.c f1.c f2.c
$ objdump -D a.out
[...]
0000000000001060 <main>:
    1060:   f3 0f 1e fa             endbr64 
    1064:   48 83 ec 08             sub    $0x8,%rsp
    1068:   48 8d 3d 99 0f 00 00    lea    0xf99(%rip),%rdi <-- foo1@2008
    106f:   e8 dc ff ff ff          callq  1050 <puts@plt>
    1074:   48 8d 3d 9d 0f 00 00    lea    0xf9d(%rip),%rdi <-- foo2@2018
    107b:   e8 d0 ff ff ff          callq  1050 <puts@plt>
[...]
0000000000002008 <foo1>:
    2008:   53        'S'  <-- 1 string @ 2008
    2009:   41        'A'
    200a:   4d        'M' 
    200b:   45 5f     'E' '_'
    200d:   56        'V'
    200e:   41        'A'
    200f:   4c 55     'L' 'U'
    2011:   45        'E'
    ...

0000000000002018 <foo2>:
    2018:   53        'S'  <-- Another string @ 2018
    2019:   41        'A' 
    201a:   4d        'M' 
    201b:   45 5f     'E' '_'
    201d:   56        'V'
    201e:   41        'A'
    201f:   4c 55     'L' 'U' 
    2021:   45        'E'

Run Code Online (Sandbox Code Playgroud)

但是如果你分别在f1.cf2.c 中定义以下(命题#2):

const char *foo1 = "SAME_VALUE";
Run Code Online (Sandbox Code Playgroud)
const char *foo2 = "SAME_VALUE";
Run Code Online (Sandbox Code Playgroud)

您定义了两个指向同一字符串的指针。在这种情况下,“SAME_VALUE”可能不会重复。在下面的原始反汇编中,字符串位于地址 2004 并且foo1foo2 都指向它:

$ gcc f.c f1.c f2.c
$ objdump -D a.out
[...]
    2004:   53        'S'    <-- 1 string @ 2004
    2005:   41        'A'
    2006:   4d        'M'
    2007:   45 5f     'E' '_'
    2009:   56        'V'
    200a:   41        'A'
    200b:   4c 55     'L' 'U'
    200d:   45        'E'
[...]
0000000000001060 <main>:
    1060:   f3 0f 1e fa             endbr64 
    1064:   48 83 ec 08             sub    $0x8,%rsp
    1068:   48 8b 3d a1 2f 00 00    mov    0x2fa1(%rip),%rdi <-- 106f+2fa1=foo1@4010 
    106f:   e8 dc ff ff ff          callq  1050 <puts@plt>
    1074:   48 8b 3d 9d 2f 00 00    mov    0x2f9d(%rip),%rdi <-- 107b+2f9d=foo2@4018 
[...]
0000000000004010 <foo1>:
    4010:   04 20         <-- foo1 = @2004
[...]
0000000000004018 <foo2>:
    4018:   04 20         <-- foo2 = @2004
Run Code Online (Sandbox Code Playgroud)

为了避免与命题#1重复,GCC 提供-fmerge-all-constants

尝试合并相同的常量和相同的变量。
此选项意味着 -fmerge-constants。除了 -fmerge-constants 之外,这还考虑了例如常量初始化数组或具有整数或浮点类型的初始化常量变量。C 或 C++ 等语言要求每个变量(包括递归调用中同一变量的多个实例)具有不同的位置,因此使用此选项会导致不一致的行为。

让我们用这个标志重建命题#1。我们可以看到foo2被优化掉了,只有foo1被保留和引用:

$ gcc -fmerge-all-constants f.c f1.c f2.c
$ objdump -D a.out
[...]
0000000000001149 <main>:
    1149:   f3 0f 1e fa             endbr64 
    114d:   55                      push   %rbp
    114e:   48 89 e5                mov    %rsp,%rbp
    1151:   48 8d 3d b0 0e 00 00    lea    0xeb0(%rip),%rdi <-- 1158(RIP) + eb0 = 2008 <foo1>
    1158:   e8 f3 fe ff ff          callq  1050 <puts@plt>
    115d:   48 8d 3d a4 0e 00 00    lea    0xea4(%rip),%rdi <-- 1164(RIP) + ea4 = 2008 <foo1>
    1164:   e8 e7 fe ff ff          callq  1050 <puts@plt>
    1169:   b8 00 00 00 00          mov    $0x0,%eax
[...]
0000000000002008 <foo1>:
    2008:   53    'S' <--- foo2 optimized out, only foo1 defined
    2009:   41    'A'
    200a:   4d    'M'
    200b:   45 5f 'E' '_'
    200d:   56    'V'
    200e:   41    'A'
    200f:   4c 55 'L' 'U'
    2011:   45    'E'
Run Code Online (Sandbox Code Playgroud)