compiler - makefile
¿Qué mejoras ofrece GCC''s__builtin_malloc() `sobre el` malloc() `? (1)
Recientemente me __builtin_malloc()
funciones integradas de GCC para algunas de las funciones de administración de memoria de la biblioteca C, específicamente __builtin_malloc()
y las incorporaciones relacionadas (consulte https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html ). Al __builtin_malloc()
de __builtin_malloc()
, me preguntaba cómo podría funcionar para proporcionar mejoras de rendimiento en las rutinas de la biblioteca relacionadas con malloc()
.
Por ejemplo, si la función tiene éxito, debe proporcionar un bloque que puede ser liberado por una llamada a plain free()
ya que el puntero puede ser liberado por un módulo compilado sin __builtin_malloc()
o __builtin_free()
habilitado (o am Me equivoqué al respecto, y si se __builtin_malloc()
, ¿se deben usar las incorporaciones globalmente?). Por lo tanto, el objeto asignado debe ser algo que pueda gestionarse con las estructuras de datos con las que lidian malloc()
y free()
.
No puedo encontrar ningún detalle de cómo funciona __builtin_malloc()
o qué es exactamente lo que hace (no soy un compilador de desarrolladores, por lo que no estoy en el __builtin_malloc()
código GCC). En algunas pruebas simples en las que he intentado llamar a __builtin_malloc()
directamente, simplemente termina siendo emitido en el código del objeto como una llamada a malloc()
normal. Sin embargo, podría haber sutileza o detalles de la plataforma que no estoy proporcionando en estas simples pruebas.
¿Qué tipo de mejoras de rendimiento puede __builtin_malloc()
proporcionar en una llamada a plain malloc()
? ¿Tiene __builtin_malloc()
una dependencia de las estructuras de datos bastante complejas que utiliza la implementación malloc()
glibc? O a la inversa, ¿tiene malloc()
/ free()
glibc algún código para lidiar con los bloques que pueden ser asignados por __builtin_malloc()
?
Básicamente, ¿cómo funciona?
Creo que no hay una implementación interna especial de GCC de __builtin_malloc()
. Más bien, existe solo como una función integrada, por lo que se puede optimizar en ciertas circunstancias.
Tomemos este ejemplo:
#include <stdlib.h>
int main(void)
{
int *p = malloc(4);
*p = 7;
free(p);
return 0;
}
Si deshabilitamos las incorporaciones (con -fno-builtins
) y miramos la salida generada:
$ 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
Las llamadas a malloc
/ free
son emitidas, como se espera.
Sin embargo, al permitir que malloc
sea una función incorporada,
$ 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
Todo lo main()
fue optimizado lejos!
Esencialmente, al permitir que malloc
sea una función integrada, GCC es libre de eliminar llamadas si su resultado nunca se usa, porque no hay efectos secundarios adicionales.
Es el mismo mecanismo que permite que las llamadas "derrochadas" a printf
se cambien a llamadas a puts
:
#include <stdio.h>
int main(void)
{
printf("hello/n");
return 0;
}
Construido deshabilitado:
$ 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
Builtins habilitados:
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