2014-09-24 4 views
22

Ich wurde kürzlich auf die integrierten Funktionen von GCC für einige Speicherverwaltungsfunktionen der C-Bibliothek aufmerksam gemacht, insbesondere auf __builtin_malloc() und damit verbundene Einbauten (siehe https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html). Nachdem ich etwas über __builtin_malloc() erfahren hatte, fragte ich mich, wie es funktionieren könnte, Leistungsverbesserungen gegenüber den einfachen Bibliotheksroutinen zu bieten, die mit malloc() vergleichbar sind.Welche Verbesserungen bietet GCC's `__builtin_malloc()` über plain `malloc()`?

Zum Beispiel, wenn die Funktion erfolgreich ist, hat es einen Block zu schaffen, die durch einen Aufruf Ebene free() befreit werden kann, da der Zeiger kann durch einen Modul befreit werden, die falsch ohne __builtin_malloc() oder __builtin_free() aktiviert (oder bin kompiliert wurde ich Darüber und wenn __builtin_malloc() verwendet wird, müssen die Builtins global verwendet werden?). Daher muss das zugeordnete Objekt etwas sein, das mit den Datenstrukturen verwaltet werden kann, die malloc() und free() betreffen.

Ich kann keine Details finden, wie __builtin_malloc() funktioniert oder was es genau tut (ich bin kein Compiler-Entwickler, also spelunking durch GCC-Quellcode ist nicht in meinem Steuerhaus). In einigen einfachen Tests, in denen ich versucht habe, direkt __builtin_malloc() aufzurufen, wird es einfach im Objektcode als Aufruf an plain malloc() ausgegeben. Jedoch könnte es Feinheiten oder Plattformdetails geben, die ich in diesen einfachen Tests nicht bereitstelle.

Welche Arten von Leistungsverbesserungen können __builtin_malloc() über einen Aufruf an Plain malloc() bieten? Hat __builtin_malloc() eine Abhängigkeit von den relativ komplexen Datenstrukturen, die glibcs ​​malloc() Implementierung verwendet? Oder umgekehrt, Glibs malloc()/free() Code haben, um mit Blöcken umzugehen, die von __builtin_malloc() zugewiesen werden könnten?

Grundsätzlich, wie funktioniert es?

Antwort

30

Ich glaube, es gibt keine spezielle GCC-interne Implementierung von __builtin_malloc(). Vielmehr existiert es nur als eingebaut, so dass es unter Umständen wegoptimiert werden kann.

Nehmen Sie dieses Beispiel:

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

Wenn wir builtins (mit -fno-builtins) und Blick auf die erzeugte Ausgabe zu deaktivieren:

$ 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  

Anrufe an malloc/free emittiert werden, wie erwartet.

jedoch durch malloc ermöglicht entfernt ein builtin,

$ 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  

Alle main() wurde optimiert zu sein!

Im Wesentlichen, indem malloc erlaubt wird, ein eingebauter zu sein, GCC ist frei, Anrufe zu beseitigen, wenn sein Ergebnis nie benutzt wird, weil es keine zusätzlichen Nebenwirkungen gibt.


Es ist der gleiche Mechanismus, der "Verschwendung" ruft printf geändert werden, um Anrufe zu puts erlaubt:

#include <stdio.h> 

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

Builtins Behinderte:

$ 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  

Builtins aktiviert:

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

Interessante und nützliche Erklärung. Das wesentliche Merkmal der eingebauten Version ist also, dass sie dem Compiler ein bekanntes Verhalten garantieren kann, das es ermöglicht, sie weg zu optimieren und vielleicht auch andere Optimierungen zu erhalten ... –

+2

@DanLenski So sehe ich es. Nach einer Reihe von Experimenten ist das einzige, was ich GCC dazu bringen kann, "Spezial" mit "__builtin_malloc" zu machen, es zu optimieren. Ich habe versucht, es "0" zu übergeben, aber das Ergebnis an eine andere (externe) Funktion übergeben verursacht einen Aufruf von "malloc" zu emittieren. –

+3

Der Compiler weiß wahrscheinlich, dass das Ergebnis von __builtin_malloc nicht mit einem anderen Zeiger verknüpft ist. Nehmen wir an, Ihre Funktion hat int * p als Parameter und ruft int * q = malloc (sizeof int) auf; * q = 1; dann weiß der Compiler, dass diese Zuweisung * p nicht geändert hat. – gnasher729

Verwandte Themen