Заставить GCC использовать повторный префикс в вызове memset()

Я разрабатываю приложение, производительность которого критична. Я хочу, чтобы 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), поэтому ему не нужно каждый раз выдавать эту инструкцию.

8
задан 29 May 2012 в 17:33
поделиться