2014-09-24 77 views
22

我最近已經知道GCC的一些C庫的內存管理功能的內置函數,特別是__builtin_malloc()和相關的內置函數(請參閱https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)。在瞭解了__builtin_malloc()之後,我想知道它是如何工作的,以提高相關庫程序的普通malloc()的性能。GCC的`__builtin_malloc()`提供了比普通`malloc()`更多的改進嗎?

例如,如果函數成功,它必須提供可以通過爲普通free()呼叫被釋放,因爲指針可以通過啓用編譯時沒有__builtin_malloc()__builtin_free()模塊被釋放框(或我錯了關於這一點,如果使用__builtin_malloc(),那麼內置函數必須全局使用?)。因此,分配的對象必須是可以用簡單的malloc()free()所處理的數據結構來管理的東西。

我找不到任何有關__builtin_malloc()如何工作的細節,或者它確實做了什麼(我不是編譯器開發人員,所以通過GCC源代碼進行的探測不在我的指導下)。在一些簡單的測試中,我嘗試直接調用__builtin_malloc(),它只是簡單地以對象malloc()的調用結束。但是,這些簡單測試中可能沒有提供細節或平臺細節。

__builtin_malloc()通過調用plain malloc()可以提供哪些性能改進? __builtin_malloc()對glibc的malloc()實現使用的相當複雜的數據結構有依賴性嗎?或者相反,glibc的malloc()/free()有一些代碼來處理可能由__builtin_malloc()分配的塊嗎?

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

回答

30

我相信沒有特殊的GCC內部實現__builtin_malloc()。相反,它僅作爲內建使用,因此在某些情況下可以優化它。

藉此例如:

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

如果我們禁用建宏(與-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 <[email protected]> 
    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 <[email protected]> 
    40059c: b8 00 00 00 00   mov eax,0x0 
    4005a1: 48 83 c4 08    add rsp,0x8 
    4005a5: c3      ret  

呼叫至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  

所有main()被優化掉!

實質上,通過允許malloc成爲內置函數,GCC可以自由地消除從未使用過的調用,因爲沒有附加的副作用。


這是相同的機制,允許 「浪費」 呼籲printf被改爲調用puts

#include <stdio.h> 

int main(void) 
{ 
    printf("hello\n"); 
    return 0; 
} 

內建命令禁用:

$ 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 <[email protected]> 
    400543: b8 00 00 00 00   mov eax,0x0 
    400548: 48 83 c4 08    add rsp,0x8 
    40054c: c3      ret  

內置命令啓用:

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 <[email protected]> 
    40053e: b8 00 00 00 00   mov eax,0x0 
    400543: 48 83 c4 08    add rsp,0x8 
    400547: c3      ret  
+3

有趣和有用的解釋。所以內置版本的基本特點是它可以保證編譯器有一個已知的行爲,從而可以優化它,也可以接受其他優化... – 2014-09-24 06:36:12

+2

@DanLenski這就是我所看到的。經過大量實驗後,我可以讓GCC用'__builtin_malloc'做「特殊」的唯一事情就是優化它。我嘗試傳遞它'0',但將結果傳遞給另一個(外部)函數導致調用'malloc'來發射。 – 2014-09-24 06:38:30

+3

編譯器可能知道__builtin_malloc的結果不會與任何其他指針一起使用。假設你的函數有int * p作爲參數並且調用int * q = malloc(sizeof int); * q = 1;那麼編譯器知道這個賦值沒有修改* p。 – gnasher729 2014-09-24 09:34:17