Временные нагрузки и аппаратный предварительный выбор, они работают вместе?
При выполнении серии _mm_stream_load_si128()
звонки (MOVNTDQA
) из последовательных ячеек памяти будет ли включаться аппаратный предварительный выборщик, или я должен использовать явную предварительную выборку программного обеспечения (с подсказкой NTA), чтобы получить преимущества предварительной выборки, все еще избегая загрязнения кэша?
Я спрашиваю об этом потому, что их цели кажутся мне противоречивыми. Потоковая загрузка извлекает данные в обход кеша, в то время как средство предварительной выборки пытается предварительно извлекать данные в кеш.
При последовательной итерации большой структуры данных (обработанные данные не будут обрабатываться в течение долгого времени), для меня будет иметь смысл избегать загрязнения иерархии chache, но я не хочу подвергаться частым ~100 штрафам за цикл, потому что Сборщик бездействует.
Целевая архитектура - Intel SandyBridge
4 ответа
Согласно сообщению Патрика Фея (Intel) за ноябрь 2011 года: "На последних процессорах Intel prefetchnta переносит линию из памяти в кэш данных L1 (а не в другие уровни кеша)". Он также говорит, что вам нужно убедиться, что вы не сделали предварительную выборку слишком поздно (предварительная загрузка HW уже перенесла ее на все уровни) или слишком рано (изгнанная к тому времени, когда вы туда доберетесь).
Как обсуждалось в комментариях к OP, современные процессоры Intel имеют большой общий L3, который включает все кэши для каждого ядра. Это означает, что трафик с когерентностью кэша должен только проверять теги L3, чтобы увидеть, может ли строка кэша быть модифицирована где-то в каждом ядре L1 / L2.
IDK, как совместить объяснение Пэт Фэй с моим пониманием когерентности кэша / иерархии кэша. Я подумал, что если он пойдет в L1, то и в L3. Может быть, у тегов L1 есть какой-то флаг, чтобы сказать, что эта строка слабо упорядочена? Мое лучшее предположение, что он упрощал и говорил L1, когда это на самом деле идет только в буферах заполнения.
В этом руководстве Intel по работе с видеопамятью рассказывается о невременных перемещениях с использованием буферов загрузки / хранения, а не строк кэша. (Обратите внимание, что это может иметь место только для некэшируемой памяти.) Здесь не упоминается предварительная выборка. Это также старый, предшествующий SandyBridge. Тем не менее, он имеет эту сочную цитату:
Обычные инструкции загрузки извлекают данные из памяти USWC в единицах того же размера, что и запросы инструкций. Напротив, инструкция потоковой загрузки, такая как MOVNTDQA, обычно вытягивает полную строку кэша данных в специальный "буфер заполнения" в ЦП. Последующие потоковые загрузки будут читать из этого буфера заполнения, что приведет к гораздо меньшей задержке.
А затем в другом параграфе говорится, что типичные процессоры имеют от 8 до 10 буферов заполнения. SnB / Haswell по-прежнему имеют 10 на ядро., Опять же, обратите внимание, что это может относиться только к не кэшируемой области памяти
movntdqa
в WB (обратная запись) память не является слабо упорядоченной (см. раздел "Загрузка NT" связанного ответа), поэтому она не может быть "устаревшей". В отличие от магазинов NT, ни movntdqa
ни prefetchnta
изменить семантику упорядочения памяти в режиме обратной записи.
Я не проверял это предположение, но prefetchnta
/ movntdqa
на современном процессоре Intel может загружать строку кэша в L3 и L1, но может пропускать L2 (потому что L2 не является включающим или исключающим L1). Подсказка NT может дать эффект, поместив строку кэша в позицию LRU своего набора, где это следующая строка, которая будет удалена. (Обычная политика кэширования вставляет новые строки в позиции MRU, наиболее удаленные от выселения. См. Эту статью об адаптивной политике L3 IvB для получения дополнительной информации о политике вставки кэша).
Пропускная способность для предварительной загрузки на IvyBridge составляет всего один на 43 цикла, поэтому будьте осторожны, чтобы не выполнять предварительную выборку слишком сильно, если вы не хотите, чтобы предварительные выборки замедляли работу кода на IvB. Источник: таблицы Инсна и руководство по микроархам Агнера Фога. Это ошибка производительности, характерная для IvB. В других разработках слишком большая предварительная выборка просто потребует пропускную способность uop, которая могла бы быть полезными инструкциями (кроме вреда от предварительной выборки бесполезных адресов).
О предварительной загрузке SW в целом (не nt
добрый): Линус Торвальдс написал о том, что они редко помогают в ядре Linux и часто приносят больше вреда, чем пользы. По-видимому, предварительная выборка NULL-указателя в конце связанного списка может вызвать замедление, потому что он пытается заполнить TLB.
Я недавно сделал несколько тестов различных prefetch
вкусы, отвечая на другой вопрос, и мои выводы были:
Результаты использования prefetchnta
были совместимы со следующей реализацией на клиенте Skylake:
prefetchnta
загружает значения вL1
а такжеL3
но неL2
(на самом деле, кажется, что линия может быть выселена изL2
если это уже есть).- Кажется, что значение "нормально" загружается в L1, но более слабым способом в L3, так что оно высвобождается быстрее (например, только одним способом в наборе, или с его установленным флагом LRU, так что оно будет следующая жертва).
prefetchnta
Как и все другие инструкции предварительной выборки, используйте запись LFB, чтобы они не помогли вам получить дополнительный параллелизм: но подсказка NTA может быть полезна здесь, чтобы избежать загрязнения L2 и L3.
Текущее руководство по оптимизации (248966-038) утверждает, что в нескольких местах prefetchnta
действительно приносит данные в L2, но только одним способом из набора. Например, в видео кодере 7.6.2.1:
Управление кешем предварительной выборки, реализованное для видеокодера, уменьшает трафик памяти. Снижение загрязнения кэша второго уровня обеспечивается за счет предотвращения попадания одноразовых данных видеокадра в кэш второго уровня. Использование невременной инструкции PREFETCH (PREFETCHNTA) переносит данные только в один путь кэш-памяти второго уровня, тем самым уменьшая загрязнение кэш-памяти второго уровня.
Это не согласуется с моими результатами тестов на Skylake, где он преодолел область 64 КиБ с 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 КиБ), но приведенные выше результаты показывают, что это не так.
Вы можете запустить эти тесты на своем собственном оборудовании в Linux, используя мою программу uarch-bench. Результаты для старых систем были бы особенно интересны.
Skylake Server (SKLX)
Сообщаемое поведение prefetchnta
на сервере Skylake, который имеет другую архитектуру кэша L3, значительно отличается от клиента Skylake. В частности, пользователь Mysticial сообщает, что строки выбираются с использованием prefetchnta
недоступны на каком-либо уровне кэша и должны быть повторно считаны из DRAM после их удаления из L1.
Наиболее вероятное объяснение состоит в том, что они вообще не входили в L3 в результате prefetchnta
- это вероятно, поскольку на сервере Skylake L3 является не включающим общий кэш-память жертвы для частных кешей L2, поэтому линии, которые обходят кеш L2, используют prefetchnta
скорее всего, никогда не будет возможности войти в L3. Это делает prefetchnta
оба более чисты в функции: меньше уровней кэша загрязнены prefetchnta
запросы, но и более хрупкие: любой сбой при чтении nta
строка от L1 до ее выселения означает еще один полный возврат в память: начальный запрос, инициированный prefetchnta
полностью впустую.
Этот вопрос заставил меня кое-что почитать... Глядя на руководство Intel для MOVNTDQA (в редакции от Sep'14), есть интересное утверждение -
Реализация процессора может использовать невременный намек, связанный с этой инструкцией, если источником памяти является тип памяти WC (с комбинированием записи). Реализация также может использовать невременный намек, связанный с этой инструкцией, если источником памяти является тип памяти WB (с обратной записью).
а позже -
Тип памяти читаемой области может переопределить невременный намек, если адрес памяти, указанный для невременного чтения, не является областью памяти WC.
Так что, похоже, нет никакой гарантии, что невременная подсказка будет делать что-либо, если только ваш тип mem не WC. Я действительно не знаю, что означает комментарий WB memtype, может быть, некоторые процессоры Intel позволяют вам использовать его для уменьшения загрязнения кэша, или, возможно, они хотели сохранить эту опцию в будущем (так что вы не начинаете использовать MOVNTDQA на WB mem и предположим, что он всегда будет вести себя одинаково), но совершенно очевидно, что WC mem - это реальный вариант использования. Вы хотите, чтобы эта инструкция обеспечивала некоторую кратковременную буферизацию для вещей, которые в противном случае были бы полностью не кэшируемыми.
Теперь, с другой стороны, посмотрим на описание для предварительной выборки *:
Предварительные выборки из некэшируемой памяти или памяти WC игнорируются.
Так что это в значительной степени закрывает историю - ваше мышление абсолютно правильно, эти два, вероятно, не предназначены и вряд ли будут работать вместе, есть вероятность, что один из них будет проигнорирован.
Хорошо, но есть ли шанс, что эти 2 действительно сработают (если процессор реализует загрузку NT для памяти WB)? Что ж, читая из MOVNTDQA еще раз, что-то еще бросается в глаза:
Любые строки с псевдонимами в кеше памяти будут отслеживаться и очищаться.
Уч. Так что, если вам каким-то образом удастся выполнить предварительную выборку в кэш, вы, скорее всего, снизите производительность любой последовательной потоковой загрузки, так как она должна сначала очистить строку. Не красивая мысль.
И то и другое MOVNTDQA
(в памяти WC) и PREFETCHNTA
не затрагивать и не запускать какие-либо аппаратные средства предварительной выборки кеша. Вся идея невременного намека - полностью избежать загрязнения кеша или, по крайней мере, минимизировать его, насколько это возможно.
Существует только очень небольшое количество (недокументированных) буферов, называемых буферами потоковой загрузки (они отделены от буферов заполнения строки и от кэша L1) для хранения строк кэша, извлеченных с использованием MOVNTDQA
, Так что в основном вам нужно использовать то, что вы получаете почти сразу. К тому же, MOVNTDQA
работает только в памяти WC.
PREFETCHNTA
Инструкция идеально подходит для вашего сценария, но вы должны выяснить, как правильно использовать ее в своем коде. Из руководства по оптимизации Intel, раздел 7.1:
Если ваш алгоритм однопроходный, используйте PREFETCHNTA. Если ваш алгоритм многопроходный, используйте PREFETCHT0.
PREFETCHNTA
Инструкция предлагает следующие преимущества:
- Он выбирает конкретную строку кэша, которая содержит указанный адрес, по крайней мере, в кэш L3 и / или потенциально более высокие уровни иерархии кэша (см. Ответ Би и Питера и раздел 7.3.2). На каждом уровне кэширования, в котором он кэшируется, он может / должен / с большей вероятностью считаться первым, который будет удален в случае необходимости удаления строки из набора. В реализации однопроходного алгоритма (такого как вычисление среднего большого массива чисел), который улучшен
PREFETCHNTA
более поздние предварительно выбранные строки кэша могут быть помещены в тот же блок, что и те строки, которые также были предварительно выбраны с использованиемPREFETCHNTA
, Таким образом, даже если общий объем извлекаемых данных огромен, будет затронут только один путь из всего кэша. Данные, которые хранятся другими способами, останутся кэшированными и будут доступны после завершения алгоритма. Но это обоюдоострый меч. Если дваPREFETCHNTA
инструкции находятся слишком близко друг к другу, и если указанные адреса отображаются на один и тот же набор кеша, то выживет только один. - Кэшированные строки, предварительно выбранные с помощью
PREFETCHNTA
сохраняются согласованными, как и любые другие кэшированные строки, используя тот же аппаратный механизм согласования. - Работает с типами памяти WB, WC и WT. Скорее всего, ваши данные хранятся в памяти WB.
- Как я уже говорил, он не запускает аппаратную предварительную выборку. Именно по этой причине его также можно использовать для повышения производительности нерегулярных шаблонов доступа к памяти в соответствии с рекомендациями Intel.
Поток, который выполняет PREFETCHNTA
может быть не в состоянии эффективно использовать его в зависимости от поведения любых других работающих потоков на том же физическом ядре, на других физических ядрах того же процессора или на ядрах других процессоров, которые совместно используют одну и ту же область когерентности. Такие методы, как закрепление, повышение приоритета, разделение кэша на основе CAT и отключение гиперпоточности, могут помочь этому потоку работать эффективно. Обратите внимание, что PREFETCHNTA
классифицируется как спекулятивная нагрузка и соответствует трем инструкциям по забору.