Невременные нагрузки и предварительный выбор оборудования, они работают вместе?
При выполнении серии вызовов _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
, полностью теряется.