Можно ли «прервать» при загрузке регистра из памяти, а не вызвать ошибку страницы? - PullRequest
0 голосов
/ 07 сентября 2018

Я имею в виду ' Минимизация ошибок страницы (и ошибок TLB) при "обходе" большого графика '

' Как узнать, находится ли указатель в физической памяти или он вызовет сбой страницы? ' - это связанный вопрос, рассматривающий проблему с другой стороны, но не имеющий решения.

Я хотел бы иметь возможность загружать некоторые данные из памяти в регистр, но иметь прерывание загрузки вместо получения ошибки страницы, если память в настоящее время выгружается. Мне нужен код для работы в пользовательском пространстве как в Windows, так и в Linux без каких-либо стандартных разрешений.

( В идеале , я также хотел бы отменить ошибку TLB.)

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

К сожалению, нет инструкции, которая просто запрашивает TLB или текущую таблицу страниц с результатом в регистре на x86 (или любом другом ISA, о котором я знаю). Возможно, так и должно быть, потому что это может быть реализовано очень дешево.


Будет ли полезна такая функция ЦП? вероятно, да для нескольких случаев

Такую вещь было бы трудно использовать способом, который окупился, потому что каждая «ложная» попытка - это время / команды процессора, которые не выполняли никакой полезной работы. Но такой случай может быть выигрышным, когда вам все равно, в каком порядке вы проходите дерево / граф, и некоторые узлы могут быть горячими в кеше, TLB или даже просто в ОЗУ, а другие холодными или даже выгруженными. на диск.

Когда памяти мало, прикосновение к холодной странице может даже привести к вытеснению горячей страницы, прежде чем вы перейдете к ней.

Обычные процессоры (например, современные x86) могут выполнять спекулятивные / неупорядоченные обходы страниц (для заполнения записей TLB) и определенно спекулятивные загрузки в кеш, но не сбои страниц. Ошибки страницы обрабатываются программным обеспечением ядром. Взятие ошибки страницы не может происходить спекулятивно, и это сериализация. (Процессоры не переименовывают уровень привилегий.)

Таким образом, программная предварительная выборка может дешево заставить аппарат заполнить TLB и кеш, пока вы касаетесь другой памяти, если вы, которую вы собираетесь касаться 2-й, были холодными . Если было жарко, и вы сначала дотрагиваетесь до холодной стороны, это прискорбно. Если существует дешевый способ проверки «горячего / холодного», возможно, стоит использовать его, чтобы всегда идти по правильному пути (по крайней мере, на первом шаге) в порядке обхода, когда один указатель горячий, а другой - холодный. Если транзакция только для чтения не является достаточно дешевой, возможно, не стоит использовать умный ответ Маргарет.

Если у вас есть 2 указателя, вы в конечном итоге будете разыменовывать, и один из них будет указывать на страницу, которая была выгружена, в то время как другая горячая, лучше всего было бы как-то обнаружить это и заставить ОС начать подкачку на одной странице. с диска в фоновом режиме, пока вы пересекаете сторону, которая уже находится в оперативной памяти. (например, с Windows PrefetchVirtualMemory или Linux madvise(MADV_WILLNEED). См. Ответы на другой вопрос ОП: Минимизация ошибок на странице (и ошибок TLB) при «обходе» большого графика )

Для этого потребуется системный вызов, но системные вызовы стоят дороже и загрязняют кэши + TLB, особенно на нынешнем x86, где смягчение Specter + Meltdown добавляет тысячи тактовых циклов. Так что не стоит делать системный вызов предварительной выборки виртуальных машин для одного из каждой пары указателей в дереве. Вы получите значительное замедление для случаев, когда все указатели были в оперативной памяти.


Возможности конструкции процессора

Как я уже сказал, я не думаю, что какие-либо текущие ISA имеют это, но я думаю, что было бы легко поддерживать в аппаратных средствах инструкции, которые запускают подобные инструкции загрузки, но дают результат, основанный на поиске TLB вместо выборка данных из кэша L1d.

На ум приходит пара вариантов:

  • a queryTLB m8 инструкция, которая записывает флаги (например, CF = 1 для настоящего времени) в зависимости от того, является ли операнд памяти в данный момент горячим в TLB (включая TLB 2-го уровня), никогда не выполняя обход страницы. И querypage m8, который будет выполнять обход страницы при пропадании TLB и устанавливает флаги в зависимости от того, есть ли запись в таблице страниц. Поместить результат в r32 целочисленный регистр, который вы можете проверить / jcc, также будет вариант.

  • a try_load r32, r/m32 инструкция, которая выполняет нормальную загрузку, если это возможно, но устанавливает флаги вместо принятия ошибки страницы, если при просмотре страницы не найдено допустимой записи для виртуального адреса. (например, CF = 1 для действительного, CF = 0 для прерывания с целочисленным результатом = 0, например rdrand . Это может пригодиться и установить другие флаги (SF / ZF / PF) в соответствии со значением, если есть один.)

Идея query будет полезна только для производительности, а не для корректности, потому что всегда будет разрыв между запросом и использованием, во время которого страница может быть отображена.(Как и системный вызов IsBadXxxPtr Windows, за исключением того, что он, вероятно, проверяет логическую карту памяти, а не таблицы аппаратных страниц.)

A try_load insn, которая также устанавливает / сбрасывает флагивместо того, чтобы поднимать #PF, можно избежать состояния гонкиУ вас могут быть разные его версии, или может потребоваться немедленный выбор условия прерывания (например, промах TLB без попытки обхода страницы).

Эти инструкции могут легко декодироваться в загрузочный файл, возможно, только в одну.Порты загрузки на современном x86 уже поддерживают нормальные загрузки, предварительную выборку программного обеспечения, широковещательные загрузки, нулевые или расширяющие знак нагрузки (movsx r32, m8 - это один моп для порта загрузки на Intel) и даже vmovddup ymm, m256 (две широковещательные трансляции на линии).) по какой-то причине, поэтому добавление другого типа load uop не кажется проблемой.

Загрузки, которые попадают в запись TLB, для которой у них нет разрешения (отображение только для ядра), в настоящее время ведут себя особеннонекоторые харчи x86 (те, которые не уязвимы для Meltdown).См. Микроархитектура за расплавлением на крове Генри Вонга (stuffedcow.net).Согласно его тестированию, некоторые процессоры выдают ноль для умозрительного выполнения более поздних инструкций после пропуска TLB / страницы (запись отсутствует).Итак, мы уже знаем, что выполнение чего-либо с результатом попадания / пропуска TLB должно повлиять на целочисленный результат загрузки.(Конечно, промах TLB отличается от попадания в привилегированную запись.)

Установка флагов из нагрузки не является чем-то, что обычно происходит на x86 (только с нагрузкой с микроплавлением + alu), поэтомувозможно, это будет реализовано с помощью ALU uop, если бы Intel когда-либо реализовывала эту идею.

Прерывание при условии, отличном от пропуска TLB / page или пропуска L1d, потребовало бы использования внешних уровней кэша для поддержки этой специальной функции.просьба, хотя.Try_load, который запускается, если он попадает в кэш L3, но прерывается при пропадании L3, потребует поддержки из кэша L3.Я думаю, что мы могли бы обойтись без этого.

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

Я подозреваю, что попытка ветвления при промахах кэша L3 обойдется вам слишком дорого в промахах ветвления, чтобы это действительно стоило того, чтобы позволить exec-out-exec-порядку выполнить свое дело.Особенно, если у вас есть гиперпоточность, чтобы этот процесс, связанный с задержкой, мог происходить на одном логическом ядре ЦП, которое также делает что-то еще.

0 голосов
/ 07 сентября 2018

Часть RTM (Restricted Transactional Memory) функции TXT-NI позволяет подавлять исключения:

Любая ошибка или ловушка в транзакционной области, которая должна подвергаться программному обеспечению, будет подавлена. транзакционный выполнение прервется , и выполнение перейдет к нетранзакционному выполнению, как если бы сбой или ловушка никогда не были произошло.
[...]
Синхронные исключительные события (#DE, #OF, #NP, #SS, #GP, #BR, #UD, #AC, #XM, #PF, #NM, #TS, #MF, #DB, # BP / INT3 ) которые происходят во время выполнения транзакции, может привести к тому, что выполнение не будет зафиксировано транзакцией, требуют нетранзакционного исполнения. Эти события подавлены, как будто они никогда не происходили.

Я никогда не использовал RTM, но он должен работать примерно так:

xbegin fallback

  ; Don't fault here

xend

; Somewhere else
fallback:
  ; Retry non-transactionally

Обратите внимание, что транзакция может быть прервана по многим причинам, см. Главу 16.8.3.2 тома 1 руководства Intel. Также обратите внимание, что RTM не является вездесущим.

Помимо RTM, я не могу придумать другой способ подавления нагрузки, поскольку он должен возвращать значение или в конечном итоге сигнализировать об аварийном завершении (которое будет таким же, как #PF).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...