Даже будучи очень похожим, одна и та же функция дает другую оптимизацию при добавлении restrict
или __attribute__((malloc))
.Рассматривая этот пример (включенный здесь в качестве ссылки на хороший пример __attribute__((malloc))
):
#include <stdlib.h>
#include <stdio.h>
int a;
void* my_malloc(int size) __attribute__ ((__malloc__))
{
void* p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable
and discarded during compilation process\n");
return 0;
}
И этот (тот же код без атрибутов):
void* my_malloc(int size);
int a;
void* my_malloc(int size)
{
void* p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable
and discarded during compilation process\n");
return 0;
}
Как и следовало ожидать, код с атрибутом malloc лучше оптимизирован (как с -O3
), чем без него.Позвольте мне включить только различия:
без атрибута:
[...]
call ___main
movl $4, (%esp)
call _malloc
testl %eax, %eax
je L9
movl $0, _a
xorl %eax, %eax
leave
.cfi_remember_state
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
L9:
.cfi_restore_state
movl $LC0, (%esp)
call _puts
movl $1, (%esp)
call _exit
.cfi_endproc
[...]
с атрибутом:
[...]
call ___main
movl $4, (%esp)
call _my_malloc
movl $0, _a
xorl %eax, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
[...]
Тем не менее, использование restrict
в этом случае бесполезнопри условии, что он не оптимизирует сгенерированный код. Если мы изменим исходный код для использования с restrict
:
void *restrict my_malloc(int size);
int a;
void *restrict my_malloc(int size)
{
void *restrict p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable and discarded \
during compilation process\n");
return 0;
}
Код asm точно такой же, как сгенерированный без атрибута malloc:
[...]
call ___main
movl $4, (%esp)
call _malloc
testl %eax, %eax
je L9
movl $0, _a
xorl %eax, %eax
leave
.cfi_remember_state
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
L9:
.cfi_restore_state
movl $LC0, (%esp)
call _puts
movl $1, (%esp)
call _exit
.cfi_endproc
[...]
Таким образом, для функций, подобных malloc / calloc, использование __attribute__((__malloc__))
выглядит более полезным, чем restrict
.
__attribute__((__malloc__))
и restrict
ведут себя по-разномуоптимизировать код, даже будучи их определения довольно похожи.Это заставляет меня думать, что нет смысла «объединять» их, учитывая, что компилятор выполняет разные оптимизации разными способами.Даже если оба они используются в одном и том же времени, сгенерированный код не будет более оптимизирован, чем наиболее оптимизированный код с одним из них (__attribute__((__malloc__))
или restrict
, в зависимости от случая).Так что выбор программиста, чтобы узнать, какой из них лучше подходит в соответствии с его / ее кодом.
Почему __attribute__((__malloc__))
не является стандартным?Я не знаю, но IMO, эти сходства с точки зрения определения и различия с точки зрения поведения не помогают интегрировать оба в стандарт с ясным, хорошо дифференцированным и общим языком.