Невременные нагрузки и предварительный выбор оборудования, они работают вместе?

При выполнении серии вызовов _mm_stream_load_si128() (MOVNTDQA) из последовательных мест памяти будет выполняться предварительный выбор аппаратного обеспечения, или я должен использовать явную предварительную выборку программного обеспечения (с подсказкой NTA), чтобы получить преимущества предварительной выборки, все еще избегая загрязнения кэша?

Я прошу об этом потому, что их цели кажутся мне противоречивыми. Потоковая загрузка будет извлекать данные в обход кеша, в то время как pre-fetcher пытается проактивно извлекать данные в кеш.

При последовательном повторении большой структуры данных (обработанные данные не будут ретушироваться в течение длительного времени), мне было бы разумно избегать загрязнения иерархии chache, но я не хочу подвергать частым штрафам за 100 циклов потому что pre-fetcher не работает.

Целевая архитектура - это Intel SandyBridge

Ответ 1

Согласно Патрик Фей (Intel), ноябрь 2011 года:, "На последних процессорах Intel prefetchnta выводит строку из памяти в L1 кэш данных (а не к другим уровням кэша)." Он также говорит, что вам нужно убедиться, что вы не префикс слишком поздно (предварительная выборка HW уже перенесла его на все уровни), или слишком рано (выселение к тому времени, когда вы туда попадете).


Как обсуждалось в комментариях к OP, текущие процессоры Intel имеют большой общий L3, который включает в себя все кешированные ядра. Это означает, что трафик кеш-когерентности должен проверять только теги L3, чтобы увидеть, может ли строка кэша быть изменена где-то в L1/L2 для каждого ядра.

IDK, как согласовать объяснение Пэта Файя с моим пониманием иерархии кеш-кеширования/кеширования. Я думал, что если он пойдет в L1, ему тоже придется идти в L3. Возможно, у тегов L1 есть какой-то флаг, чтобы сказать, что эта строка слабо упорядочена? Мое лучшее предположение заключается в том, что он упрощал и говорил L1, когда он на самом деле поступает только в буферах заполнения.

Этот Руководство Intel по работе с видеопамятью рассказывает о невременных перемещениях с использованием буферов загрузки/хранения, а не строк кеша. (Обратите внимание, что это может иметь место только для неприкасаемой памяти.) Он не упоминает предварительную выборку. Он также старый, предшествовавший SandyBridge. Однако у него есть эта сочная цитата:

Обычные инструкции загрузки извлекают данные из USWC-памяти в единицах того же размера запрашивается инструкция. Напротив, потоковая нагрузка такая инструкция, как MOVNTDQA, обычно вытягивает полную строку кеша данных в специальный "буфер заполнения" в CPU. Последующие потоковые нагрузки будет считывать из этого заполняющего буфера, обеспечивая гораздо меньшую задержку.

И затем в другом абзаце говорится, что типичные процессоры имеют от 8 до 10 буферов заполнения. У SnB/Haswell все еще есть 10 на ядро.. Опять же, обратите внимание, что это может относиться только к областям памяти, не подлежащим анализу.

movntdqa на WB (обратная запись) память не является слабо упорядоченной (см. раздел загрузки NT связанного ответа), поэтому ему не разрешено быть "устаревшим". В отличие от хранилищ NT, ни movntdqa, ни prefetchnta не изменяет семантику упорядочения памяти памяти Write-Back.

Я не тестировал это предположение, но prefetchnta/movntdqa на современном процессоре Intel мог загружать линию кэша в L3 и L1, но мог пропустить L2 (потому что L2 не включительно или исключая L1). Подсказка NT может иметь эффект, поместив строку кэша в позицию LRU своего набора, где будет выведена следующая строка. (Обычная политика кэширования вставляет новые строки в позицию MRU, наиболее удаленную от выселения. См. в этой статье о IvB-адаптивной политике L3 для получения дополнительной информации о политике вставки кеша).


Производительность предварительной выборки в IvyBridge составляет только один на 43 цикла, поэтому будьте осторожны, чтобы не префикс слишком сильно, если вы не хотите, чтобы префешировки замедляли ваш код на IvB. Источник: Agner Fog insn tables и руководство микроархива. Это ошибка производительности, характерная для IvB. В других проектах слишком большая предварительная выборка просто займет пропускную способность uop, которая могла бы быть полезными инструкциями (кроме вреда от предварительной выборки бесполезных адресов).

О предварительной выборке SW в целом (а не в типе nt): Линус Торвальдс рассказал о том, как они редко помогают в ядре Linux и часто делают больше вреда, чем пользы. По-видимому, предварительная выборка указателя NULL в конце связанного списка может привести к замедлению, поскольку он пытается заполнить TLB.

Ответ 2

Этот вопрос заставил меня немного почитать... Глядя на руководство Intel для MOVNTDQA (используя издание Sep'14), есть интересное выражение -

Реализация процессора может использовать невременную подсказку связанные с этой инструкцией, если источником памяти является WC (write комбинируя) тип памяти. Реализация может также использовать невременная подсказка, связанная с этой инструкцией, если память источником является тип памяти WB (write back).

а затем -

Тип памяти считываемой области может переопределять невременную подсказка, если адрес памяти, указанный для невременного чтения, не является область памяти WC.

Таким образом, нет никакой гарантии, что невременная подсказка сделает что-либо, если ваш тип mem не является WC. Я действительно не знаю, что означает комментарий WM memtype, возможно, некоторые процессоры Intel позволяют использовать его для снижения вреда от кеш-памяти, или, может быть, они хотят сохранить этот вариант на будущее (так что вы не начинаете использовать MOVNTDQA на WB mem и предположим, что он всегда будет вести себя одинаково), но совершенно ясно, что WC mem является настоящим прецедентом. Вы хотите, чтобы эта инструкция обеспечивала некоторую кратковременную буферизацию для вещей, которые в противном случае были бы полностью несовместимыми.

Теперь, с другой стороны, глядя на описание для предварительной выборки *:

Предварительные выборки из непогружаемой или WC-памяти игнорируются.

Таким образом, это почти закрывает историю - ваше мышление абсолютно правильно, эти два, вероятно, не предназначены и вряд ли будут работать вместе, есть вероятность, что один из них будет проигнорирован.

Хорошо, но есть ли шанс, что эти 2 действительно будут работать (если процессор реализует загрузки NT для WB-памяти)? Ну, снова прочитав MOVNTDQA, что-то еще бросается в глаза:

Любые строки с псевдонимом типа памяти в кеше будут отслежены и очищено.

Уч. Поэтому, если вам как-то удастся выполнить предварительную выборку в вашем кеше, вы, скорее всего, снижаете производительность любой последовательной потоковой загрузки, так как сначала нужно будет выровнять строку. Не очень хорошая мысль.

Ответ 3

Недавно я провел несколько тестов различных ароматов prefetch, а ответил на другой вопрос, и мои выводы были:

Результаты использования prefetchnta соответствовали следующей реализации для клиента Skylake:

  • prefetchnta загружает значения в L1 и L3, но не в L2 (фактически, кажется, что строка может быть выведена из L2, если она уже существует).
  • Кажется, что значение "нормально" загружается в L1, но более слабым образом в L3, так что оно выведено быстрее (например, только в одном виде в наборе или с его флагом LRU, будет следующей жертвой).
  • prefetchnta, как и все другие инструкции предварительной выборки, используйте запись LFB, поэтому они действительно не помогут вам получить дополнительные parallelism: но подсказка NTA может быть полезна здесь, чтобы избежать загрязнения L2 и L3.

В текущем руководстве по оптимизации (248966-038) в нескольких местах указано, что prefetchnta выводит данные в L2, но только в одном из них. Например, в 7.6.2.1 Video Encoder:

Управление кэшем предварительной выборки, реализованное для видеокодера уменьшает трафик памяти. Снижение загрязнения кэша второго уровня обеспечивается предотвращением входа в одноразовые видеоданные кеш второго уровня. Использование невременного PREFETCH (PREFETCHNTA) команда передает данные только одному из способов кэша второго уровня, тем самым уменьшая загрязнение кэша второго уровня.

Это не согласуется с моими результатами тестов на Skylake, где шаг над областью 64 KiB с prefetchnta показывает производительность, почти точно согласующуюся с извлечением данных из L3 (~ 4 цикла на нагрузку с коэффициентом MLP 10 и L3 латентность около 40 циклов):

                                 Cycles       ns
         64-KiB parallel loads     1.00     0.39
    64-KiB parallel prefetcht0     2.00     0.77
    64-KiB parallel prefetcht1     1.21     0.47
    64-KiB parallel prefetcht2     1.30     0.50
   64-KiB parallel prefetchnta     3.96     1.53

Так как L2 в Skylake имеет 4-позиционный путь, если данные были загружены в один конец, он должен просто оставаться в кэше L2 (один из способов охватывает 64 KiB), но приведенные выше результаты указывают на то, что он не.

Вы можете запускать эти тесты на своем собственном оборудовании в Linux, используя мою программу uarch-bench. Результаты для старых систем будут особенно интересными.

Сервер Skylake (SKLX)

Сообщенное поведение prefetchnta на сервере Skylake, которое имеет другую архитектуру кэша L3, существенно отличается от клиента Skylake. В частности, пользователь Mystical сообщает, что строки, полученные с помощью prefetchnta, недоступны ни на одном уровне кеша и должны быть перечитаны с DRAM после их изъятия из L1.

Наиболее вероятным объяснением является то, что они никогда не вводили L3 вообще в результате prefetchnta - это, вероятно, так как на сервере Skylake L3 является неинклюзивным общим кэшем для личных кэшей L2, поэтому строки, которые обход кеша L2 с использованием prefetchnta, вероятно, никогда не будет иметь шанса войти в L3. Это делает prefetchnta более чистым в функции: меньшее количество уровней кэша загрязнено запросами prefetchnta, но также более хрупким: любой отказ прочитать строку nta от L1 до того, как он выйдет, означает еще одно полное обратное перемещение в память: начальный запрос, инициированный prefetchnta, полностью теряется.