Подтвердить что ты не робот

Какие улучшения GCC `__builtin_malloc()` обеспечивают более простой `malloc()`?

Недавно я был ознакомлен с встроенными функциями 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() или что он делает (я не являюсь компилятором dev, поэтому spelunking через исходный код GCC отсутствует в моей рулевой рубке). В некоторых простых тестах, где я пытался напрямую вызвать __builtin_malloc(), он просто заканчивается тем, что в объектном коде испускается как вызов простого malloc(). Тем не менее, могут быть тонкости или детали платформы, которые я не предоставляю в этих простых тестах.

Какие улучшения производительности можно __builtin_malloc() обеспечить за счет простого malloc()? Имеет ли __builtin_malloc() зависимость от довольно сложных структур данных, используемых glibc malloc()? Или наоборот, имеет ли glibc malloc()/free() некоторый код для обработки блоков, которые могут быть выделены __builtin_malloc()?

В принципе, как это работает?

4b9b3361

Ответ 1

Я считаю, что не существует специальной внутренней реализации 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