GCC的`__builtin_malloc()`对普通`malloc()`有什么改进?

Mic*_*urr 26 c gcc

我最近了解了GCC的一些C库内存管理功能的__builtin_malloc()内置函数,特别是相关的内置函数(参见https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html) .在了解之后__builtin_malloc(),我想知道如何提供相对于普通malloc()相关库例程的性能改进.

例如,如果函数成功,它必须提供一个可以通过调用plain释放的块,free()因为指针可能被没有__builtin_malloc()__builtin_free()启用的模块释放(或者我错了,如果__builtin_malloc()使用的话) ,内置必须全局使用?).因此,分配的对象必须是可以使用简单malloc()free()处理的数据结构来管理的东西.

我找不到任何有关如何__builtin_malloc()工作或它的确切功能的详细信息(我不是编译器开发人员,所以通过GCC源代码进行探索不在我的驾驶室中).在我尝试__builtin_malloc()直接调用的一些简单测试中,它最终只是作为对plain的调用在目标代码中发出malloc().但是,在这些简单的测试中,我可能没有提供细微或平台细节.

__builtin_malloc()通过普通电话可以提供哪些性能改进malloc()?是否__builtin_malloc()依赖于glibc malloc()实现使用的相当复杂的数据结构?或者相反,glibc malloc()/ free()有一些代码来处理可能被分配的块__builtin_malloc()吗?

基本上,它是如何工作的?

Jon*_*art 33

我相信没有特别的GCC内部实施__builtin_malloc().相反,它仅作为内置存在,因此可以在某些情况下进行优化.

举个例子:

#include <stdlib.h>
int main(void)
{
    int *p = malloc(4);
    *p = 7;
    free(p);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我们禁用builtins(with -fno-builtins)并查看生成的输出:

$ gcc -fno-builtins -O1 -Wall -Wextra builtin_malloc.c && objdump -d -Mintel a.out

0000000000400580 <main>:
  400580:   48 83 ec 08             sub    rsp,0x8
  400584:   bf 04 00 00 00          mov    edi,0x4
  400589:   e8 f2 fe ff ff          call   400480 <malloc@plt>
  40058e:   c7 00 07 00 00 00       mov    DWORD PTR [rax],0x7
  400594:   48 89 c7                mov    rdi,rax
  400597:   e8 b4 fe ff ff          call   400450 <free@plt>
  40059c:   b8 00 00 00 00          mov    eax,0x0
  4005a1:   48 83 c4 08             add    rsp,0x8
  4005a5:   c3                      ret    
Run Code Online (Sandbox Code Playgroud)

按预期调用malloc/ free发出.

但是,通过允许malloc成为内置的,

$ gcc -O1 -Wall -Wextra builtin_malloc.c && objdump -d -Mintel a.out

00000000004004f0 <main>:
  4004f0:   b8 00 00 00 00          mov    eax,0x0
  4004f5:   c3                      ret    
Run Code Online (Sandbox Code Playgroud)

所有的都main()被优化了!

基本上,通过允许malloc成为内置函数,如果从未使用过结果,GCC可以自由地消除调用,因为没有额外的副作用.


这是允许将"浪费"调用printf更改为调用的相同机制puts:

#include <stdio.h>

int main(void)
{
    printf("hello\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

内置禁用:

$ gcc -fno-builtin -O1 -Wall builtin_printf.c && objdump -d -Mintel a.out

0000000000400530 <main>:
  400530:   48 83 ec 08             sub    rsp,0x8
  400534:   bf e0 05 40 00          mov    edi,0x4005e0
  400539:   b8 00 00 00 00          mov    eax,0x0
  40053e:   e8 cd fe ff ff          call   400410 <printf@plt>
  400543:   b8 00 00 00 00          mov    eax,0x0
  400548:   48 83 c4 08             add    rsp,0x8
  40054c:   c3                      ret    
Run Code Online (Sandbox Code Playgroud)

Builtins启用:

gcc -O1 -Wall builtin_printf.c && objdump -d -Mintel a.out

0000000000400530 <main>:
  400530:   48 83 ec 08             sub    rsp,0x8
  400534:   bf e0 05 40 00          mov    edi,0x4005e0
  400539:   e8 d2 fe ff ff          call   400410 <puts@plt>
  40053e:   b8 00 00 00 00          mov    eax,0x0
  400543:   48 83 c4 08             add    rsp,0x8
  400547:   c3                      ret    
Run Code Online (Sandbox Code Playgroud)

  • 你在答案中提示的是什么,但明确说明的是,对声明的函数`malloc`的调用通常被转换为对`__builtin_malloc`的调用,并且对`__builtin_malloc`的调用通常被转换为调用外部库函数`malloc`.你描述了第二部分,但第一部分也是相关的:它很重要因为它表明用户几乎从不需要实际拼出`__builtin_malloc`:即使用户只使用普通的旧` malloc`. (4认同)
  • 有趣和有用的解释.因此,内置版本的基本特征是它可以保证编译器的已知行为,从而可以对其进行优化,也可以接收其他优化... (3认同)
  • 编译器可能知道__builtin_malloc的结果不与任何其他指针别名.假设你的函数有int*p作为参数并调用int*q = malloc(sizeof int);*q = 1; 然后编译器知道这个赋值没有修改*p. (3认同)
  • @DanLenski这就是我的看法.经过多次实验,我唯一能让GCC用"__builtin_malloc"做"特殊"的就是优化它.我尝试将它传递给`0`,但是将结果传递给另一个(外部)函数会导致调用`malloc`被发出. (2认同)