Каков эффект второго аргумента в _builtin_prefetch ()?

В документе GCC указывается использование _buitin_prefetch.

Третий аргумент совершенен. Если это 0, компилятор генерирует команду prefetchtnta (% rax). Если это 1, компилятор генерирует команду prefetcht2 (% rax). Если это 2, компилятор генерирует команду prefetcht1 (% rax). Если это 3 (по умолчанию), компилятор генерирует prefetcht0 (% rax).

Если мы изменим третий аргумент, код операции уже изменился соответствующим образом.

Но второй аргумент, похоже, не имеет никакого эффекта.

__builtin_prefetch(&x,1,2); __builtin_prefetch(&x,0,2); __builtin_prefetch(&x,0,1); __builtin_prefetch(&x,0,0); 

Вышеприведенный образец кода, который генерирует:

Ниже приведена assembly:

  27: 0f 18 10 prefetcht1 (%rax) 2a: 48 8d 45 fc lea -0x4(%rbp),%rax 2e: 0f 18 10 prefetcht1 (%rax) 31: 48 8d 45 fc lea -0x4(%rbp),%rax 35: 0f 18 18 prefetcht2 (%rax) 38: 48 8d 45 fc lea -0x4(%rbp),%rax 3c: 0f 18 00 prefetchnta (%rax) 

Можно наблюдать изменение опкодов по третьему аргументу. Но даже если я изменил второй аргумент (который указывает чтение или запись), код сборки остается тем же. и . Так что это не дает никакой информации машине. Тогда в чем смысл второго аргумента?

Из той же самой ссылки, которую вы опубликовали:

Существуют два необязательных аргумента: rw и locality . Значение rw представляет собой константу времени компиляции, равную единице или нулю; один означает, что предварительная выборка готовится для записи на адрес памяти и нуль, по умолчанию означает, что предварительная выборка готовится к чтению.

Архитектура x86 не имеет никакого различия между чтением и предварительной выборкой записи.
Это не означает, что вы должны игнорировать второй аргумент, поскольку код записи на C выполняется для улучшения переносимости. Даже если в вашей машине второй аргумент не используется, его можно использовать при компиляции в разные архитектуры.

EDIT Как отметил @PeterCordes в своем комментарии, x86 действительно имеет инструкцию prefetch в ожидании записи.
Он отличается от других инструкций предварительной выборки, поскольку он делает недействительным другой кэшированный экземпляр выбранной строки (и устанавливает ее в исключительное состояние).

Как указывает Маргарет, один из аргументов – rw . Базовая x86-64 (SSE2) не включает в себя инструкцию записи prefetch, но они существуют как расширение ISA. Как обычно, компиляторы не будут использовать их, если вы не скажете, что их компилируете для целевой, которая ее поддерживает.

Две инструкции: PREFETCHW и PREFETCHWT1 .

PREFETCHW первоначально появился в 3DNow! AMD, но имеет свой собственный бит функции, так что процессоры могут указать поддержку для него, но не другие инструкции 3dNOW (упакованные float в MMX).

Я не уверен, что какие-либо процессоры поддерживают PREFETCHWT1. Основываясь на этом списке рассылки , я думаю, что это, вероятно, в Xeon PHI изначально и / или связано с AVX512.


__builtin_prefetch(p,1,2); компилируется следующим образом:

  • PREFETCHT1 без опций -m или -march=haswell или старше Intel.
  • PREFETCHW с целью AMD, например -march=k8 или -march=bdver2 (Piledriver).
  • PREFETCHW с -march=broadwell или новым семейством Intel SnB.
  • PREFETCHWT1 с -mprefetchwt1 . (Если PREFETCHW также доступен, gcc использует его для локальности = 3, но PREFETCHWT1 для локальности <= 2.)

Проверьте это на проводнике компилятора Godbolt , для -march=haswell vs. -march=broadwell -mprefetchwt1 . Или измените сами компилятор.

Как ни странно, в целевых опциях gcc x86 не упоминается отдельный переключатель для включения PREFETCHW; он включен только как часть -march=whatever . Этот ответ SO использует -mprfchw чтобы включить его.

Также обратите внимание, что его машинное кодирование 0F 0D r/m8 декодируется как многобайтовый NOP на процессорах, у которых нет PREFETCHW или 3DNow! Функция-бит. На ранних 64-битных процессорах Intel это незаконная инструкция. (В более новых версиях Windows требуется, чтобы PREFETCHW выполнялся без сбоев, и в этом контексте люди говорят о процессоре, поддерживающем PREFETCHW, даже если он работает как NOP.

Однако предпочтительнее использовать предварительную выборку для чтения, а не NOP. Но вы, вероятно, не хотите делать PREFETCHW и PREFETCHT0, потому что слишком много инструкций по предварительной выборке не очень хорошо. (особенно для Intel IvyBridge, у которого есть некоторая ошибка производительности для пропускной способности prefetch-инструкции. Но OTOH, это запустило бы PREFETCHW как NOP, так что вы получите только одну предварительную выборку в этом случае.)