Я разрабатываю приложение, производительность которого критична. Я хочу, чтобы GCC переводил некоторые конкретные вызовы memset() как инструкцию с повторным префиксом, например "rep stos QWORD PTR es:[rdi],rax". GCC делает это автоматически, когда размер известен и мал.
Однако GCC сопоставляет вызовы memset() со случайной длиной через вызов memset() через PLT, что вызывает неправильное предсказание ветвления, поскольку кэш предиктора ветвлений холодный.
Есть ли способ заставить GCC делать то, что я хочу (вне встроенного ассемблера)? Обратите внимание, что мне не нужно такое поведение для всей программы, только для некоторых конкретных вызовов memset().
Что касается связанной темы, меня также интересуют какие-либо хаки, которые предотвращают ветвление GCC, когда эту работу выполняет инструкция cmovcc (я знаю об использовании &,+ и т. д. вместо &&).
Большое спасибо за любую помощь.
@FrankH:
В общем, так я и сделал. Вот мой код:
static finline void app_zero(void *dst, uint32_t size, uint32_t count)
{
// Warning: we tell gcc to use 'dst' both as source and destination here.
// This does not cause problems because we don't reuse 'dst'.
#ifdef APP_ARCH_X86
#define STOS(X,Y) do { \
int c = (size/Y)*count; \
__asm__ __volatile__("cld; xor %%eax, %%eax; rep stos"X"\n\n" \
: "+D"(dst), "+c"(c) :: "rax", "flags"); \
} while (0)
if (size % 8 == 0) STOS("q", 8);
else if (size % 4 == 0) STOS("l", 4);
else if (size % 2 == 0) STOS("w", 2);
else STOS("b", 1);
#undef STOS
#else
memset(dst, 0, size*count);
#endif
}
Обратите внимание, что ваш пример работает в вашей тестовой настройке, но не будет работать.
в целом. GCC может изменить флаг направления, поэтому используется инструкция cld
.
необходимый.Кроме того, вы должны указать gcc, что %rdi
и %rcx
будут
изменено инструкцией stos
, и поскольку gcc не позволит вам
указать, что регистр является как входным, так и затертым, вы должны использовать
неудобный синтаксис "+"
(который также испортит ваши входные значения).
Это не оптимально из-за инструкции 'cld', которая имеет задержку 4 цикла на Nehalem. GCC внутренне отслеживает состояние регистра флага (AFAICT), поэтому ему не нужно каждый раз выдавать эту инструкцию.