具有静态分配的程序的编译时间长

Pre*_*rag 16 gcc

如果有人能告诉我为什么编译这个程序,我真的很感激:

double data[123456789];  
int main() {}
Run Code Online (Sandbox Code Playgroud)

比这个编译要长10倍:

int main() {
    double* data=new double[123456789];
}
Run Code Online (Sandbox Code Playgroud)

两者都编译时:

$ g++ -O0
Run Code Online (Sandbox Code Playgroud)

并且可执行文件的大小几乎相同.

我在Ubuntu 10.04上使用gcc 4.4.3.

谢谢.

dav*_*idg 25

动态分配

你的第二个程序在运行时分配内存; 从编译器的角度来看,编译以下任何内容之间没有真正的区别:

double *data = new double[123456789];
double *data = malloc(123456789);
double data  = sqrt(123456789);
Run Code Online (Sandbox Code Playgroud)

它们都做不同的事情,但编译器需要做的就是生成一个带有固定参数的外部函数的调用.如果您用于g++ -S生成程序集,则可以看到此内容:

.text
main:
    subq    $8, %rsp          /* Allocate stack space. */
    movl    $987654312, %edi  /* Load value "123456789 * 8" as argument. */
    call    _Znam             /* Call the allocation function. */
    xorl    %eax, %eax        /* Return 0. */
    addq    $8, %rsp          /* Deallocate stack space. */
    ret
Run Code Online (Sandbox Code Playgroud)

这对于任何编译器来说都很简单,并且可以链接任何链接器.

静态分配

不过,你已经注意到,你的第一个程序有点小问题.如果我们看看它的组件,我们会看到一些不同的东西:

.text
main:
    xorl    %eax, %eax        /* Return 0. */
    ret

.bss
data:
    .zero   987654312         /* Reserve "123456789 * 8" bytes of space. */
Run Code Online (Sandbox Code Playgroud)

生成的程序集要求123456789 * sizeof(double)在程序首次启动时保留空格字节.当它被组装并稍后链接时(在幕后发生,你只是运行g++ foo.c),链接器ld实际上将在内存中分配所有这些保留空间.这是时间的流逝.如果在运行topg++运行,您将看到ld系统内存的大量内存.

减少可执行的大小

一个合理的问题可能是"如果在链接时保留内存,为什么我的可执行文件最终不会很大?".答案隐藏在.bss装配中的标记中.这告诉链接器它下面定义的数据不应该存储在最终的可执行文件中,而是在运行时分配给零.

这给我们留下了以下一系列步骤:

  1. 汇编器告诉链接器它需要创建一个1GB长的内存段.

  2. 链接器继续并分配此内存,以准备将其放入最终的可执行文件中.

  3. 链接器意识到此内存位于.bss节中并被标记NOBITS,这意味着数据只是0,并且不需要物理放入最终的可执行文件中.它避免写出1GB的数据,而只是抛弃分配的内存.

  4. 链接器只向编译的代码写入最终的ELF文件,生成一个小的可执行文件.

更智能的链接器可能能够避免上面的步骤2和3,从而使编译时间更快.实际上,像你这样的场景在实践中经常不足以使这样的优化变得有价值.

动态与静态分配

如果您正在尝试解决上述哪一项(动态与静态分配)在您的程序中实际使用,请参考以下几点:

  • 链接器需要使用与最终程序一样多的内存(加上一点).如果要静态分配4GB的RAM,则链接器需要4GB的RAM.这并不隐含于链接器的工作方式,而只是它们的实现方式.

  • 分配大量内存时,动态执行此操作可以让您更好地进行错误处理(在屏幕上显示一条用户友好的消息,说明您没有足够的内存,而不是仅仅通过操作系统的消息加载可执行文件给用户);

  • 动态分配内存允许您根据实际需要选择要分配的内存量.(如果data是缓存,用户可以选择缓存大小,如果存储中间结果,则可以根据问题调整大小等)

  • 动态分配内存允许您稍后释放它,如果您的程序需要继续并在完成内存后执行更多工作.

最后,如果以上几点无关紧要,你可以处理更长的编译时间,那可能并不重要.静态分配内存可以简单得多,并且通常是少量内存或丢弃应用程序的正确方法.