Что происходит после промаха L2 TLB?

Я пытаюсь понять, что происходит, когда первые два уровня буфера Translation Lookaside приводят к промахам?

Я не уверен, происходит ли "прохождение страницы" в специальной аппаратной схеме или хранятся ли таблицы страниц в кеше L2/L3 или они только находятся в основной памяти.

Ответ 1

Современные микроархитектуры x86 имеют специальное оборудование для просмотра страниц. Они могут даже спекулятивно выполнять обход страниц для загрузки записей TLB до того, как пропадание TLB действительно произойдет. Skylake может даже совершать две прогулки сразу, см. раздел 2.1.3 руководства по оптимизации Intel. Это может быть связано с падением штрафа за нагрузку при разделении страниц со 100 до 5 циклов.

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

(Win9x зависел от этого, и неразрывность важного существующего кода - это то, о чем заботятся производители ЦП. Когда Win9x был написан, текущие правила недействительности TLB еще не существовали, поэтому это даже не было ошибкой; см. комментарии Энди Глеу, цитируемые ниже). Семейство AMD Bulldozer нарушает это предположение, давая вам только то, что написано в руководствах по x86 на бумаге.


Нагрузки таблиц страниц, генерируемые оборудованием для просмотра страниц, могут попадать в кеши L1, L2 или L3. Счетчики перфорации Broadwell, например, могут подсчитывать попадания при просмотре страниц в выбранных вами L1, L2, L3 или память (то есть отсутствует кеш). Oprofile называет это page_walker_loads.

Поскольку таблицы страниц используют формат дерева оснований с записями каталога страниц, указывающими на таблицы записей таблицы страниц, PDE более высокого уровня (записи каталога страниц) могут быть полезны для кэширования в оборудовании для обхода страниц.. Это означает, что вам нужно очищать TLB в тех случаях, когда вы можете подумать, что вам не нужно. Intel и AMD фактически делают это, согласно этому документу (раздел 3).

В этой статье говорится, что загрузка страниц на процессорах AMD игнорирует L1, но проходит через L2. (Возможно, чтобы избежать загрязнения L1 или уменьшить конкуренцию за чтение портов). В любом случае, это делает кэширование нескольких высокоуровневых PDE (каждый из которых охватывает много разных записей перевода) внутри аппаратного обеспечения просмотра страниц еще более ценным, поскольку цепочка поиска указателей обходится дороже с более высокой задержкой.

Но обратите внимание, что x86 не гарантирует отрицательного кэширования записей TLB. Для изменения страницы с недействительной на действительную не требуется invlpg. (Таким образом, если реальная реализация действительно хочет выполнять такого рода негативное кэширование, она должна отслеживать или каким-либо образом реализовывать семантику, гарантированную руководствами по x86.)

(Историческая справка: Энди Глью ответил на дубликат этого вопроса по электронике. SE говорит, что в P5 и более ранних версиях аппаратные загрузки страниц обходили внутренний кэш L1 (это было обычно сквозной записи, так что это делало PageWalk согласованным с магазинами.) IIRC, моя материнская плата Pentium MMX имела кэш L2 на mobo, возможно, как кэш на стороне памяти. Энди также подтверждает, что P6 и более поздние версии загружаются из обычного L1d-кэша.

Этот другой ответ также содержит несколько интересных ссылок в конце, включая статью, на которую я ссылался в конце последнего абзаца. Также кажется, что ОС может обновлять сам TLB, а не таблицу страниц, при сбое страницы (HW pagewalk не находит запись), и задается вопросом, можно ли отключить просмотр страниц HW на x86. (Но на самом деле ОС просто изменяет таблицу страниц в памяти, и при возврате из #PF повторно запускается неисправная инструкция, так что на этот раз HW pagewalk преуспеет.) Возможно, в статье рассматриваются ISA, такие как MIPS, где программное управление TLB управляет/пропускается. обработка возможна.

Я не думаю, что на самом деле можно отключить HW PageWalk на P5 (или любой другой x86). Это может потребовать от программного обеспечения обновления записей TLB с помощью специальной инструкции (ее нет) или с помощью wrmsr или хранилища MMIO. В замешательстве Энди говорит (в потоке, который я цитировал ниже), что программная обработка TLB была быстрее на P5. Я думаю, что он имел в виду, был бы быстрее, если бы это было возможно. Он работал в Imation (на MIPS) в то время, когда обход SW-страницы является опцией (иногда единственной), в отличие от x86 AFAIK.


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

Кстати, вероятно, было бы возможно создать процессор x86, который обрабатывает пропуски TLB, перехватывая микрокод вместо того, чтобы выделять аппаратный конечный автомат. Это будет (намного?) Менее производительным и, возможно, не стоит запускать спекулятивно (поскольку выдача мопов из микрокода означает, что вы не можете выдавать инструкции из выполняющегося кода.)

Микрокодированная обработка TLB теоретически может быть не страшной, если вы запускаете эти мопы в отдельном аппаратном потоке (интересная идея) в стиле SMT. Чтобы переключаться с однопоточного режима на оба активных логических ядра (нужно подождать, пока что-то истечет, пока он не сможет разделить ROB, сохранить очередь и т.д.), Вам потребуется гораздо меньше затрат на запуск/остановку, чем при обычной гиперпоточности, поскольку он запускается/останавливается очень часто по сравнению с обычным логическим ядром. Но это может быть возможно, если это на самом деле не полностью отдельный поток, а просто какое-то отдельное состояние удаления, поэтому пропущенные в нем кэши не блокируют удаление основного кода и позволяют ему использовать пару скрытых внутренних регистров для временных. Код, который он должен выполнить, выбирается разработчиками ЦП, поэтому дополнительный поток HW не должен приближаться к полному архитектурному состоянию ядра x86. Ему редко приходится делать какие-либо хранилища (может быть, только для флагов доступа в PTE?), Поэтому было бы неплохо позволить этим хранилищам использовать ту же очередь хранилищ, что и в основном потоке. Вы просто разделили бы внешний интерфейс, чтобы смешать его в ментах TLB-управления и позволить им работать не по порядку с основным потоком. Если бы вы могли сохранить малое количество мопов на переходе на страницу, это может не сработать.

На самом деле ни один из процессоров не выполняет "HW" обхода страниц с микрокодом в отдельном HW-потоке, о котором я знаю, но это теоретическая возможность.


В некоторых архитектурах RISC (например, MIPS) ядро ОС отвечает за обработку пропусков TLB. Отсутствие TLB приводит к выполнению обработчика прерываний пропуска TLB ядра. Это означает, что ОС может определять собственный формат таблицы страниц на таких архитектурах. Я полагаю, что пометка страницы как грязной после записи также требует прерывания для подпрограммы, предоставляемой ОС, поскольку ЦП не знает о формате таблицы страниц.

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

Другие ссылки:


Комментарии о связности TLB от Энди Глеу, одного из архитекторов Intel P6 (Pentium Pro/II/III), затем работал в AMD.

Основная причина, по которой Intel начала работать с таблицами страниц, а не обходить кеш, заключалась в производительности. До P6 страницы пошли медленно, не извлекали выгоду из кеша и не носили спекулятивный характер. Достаточно медленно, чтобы программная обработка ошибок TLB была выигрышем в производительности 1. P6 ускоряет TLB, выполняя их спекулятивно, используя кеш, а также кэшируя промежуточные узлы, такие как записи каталога страниц.

Кстати, AMD неохотно делала спекуляции с ошибками TLB. Я думаю, потому что они были под влиянием архитекторов DEC VAX Alpha. Один из архитекторов DEC Alpha очень убедительно сказал мне, что умозрительная обработка промахов TLB, таких как P6, была неправильной и никогда не сработает. Когда я прибыл в AMD примерно в 2002 году, у них все еще было что-то, называемое "Забор TLB" - не инструкция по забору, а точка в последовательности rop или микрокодов, где TLB пропускается, может или не может произойти - я боюсь, что я не помню точно, как это работает.

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

Напомним, что когда P6 был запущен, P5 не поставлялся: все существующие x86 все делали обход таблицы кэширования, обходя их в порядке, не спекулятивно, без асинхронных предварительных выборок, но при записи через кэши. То есть Они были согласованы с кешем, и ОС могла рассчитывать на детерминированную замену записей TLB. IIRC Я написал эти архитектурные правила о спекулятивной и недетерминированной кешируемости, как для записей TLB, так и для кэшей данных и инструкций. Вы не можете винить ОС, такие как Windows, UNIX и Netware, в том, что они не следуют правилам таблицы страниц и правилам управления TLB, которых не было в то время.

IIRC Я написал эти архитектурные правила о спекулятивной и недетерминированной кешируемости, как для записей TLB, так и для кэшей данных и инструкций. Вы не можете винить ОС, такие как Windows, UNIX и Netware, в том, что они не следуют правилам таблицы страниц и правилам управления TLB, которых не было в то время.

Сноска 1: Насколько мне известно, ни один процессор x86 не поддерживает программное управление TLB. Я думаю, что Энди хотел сказать "было бы быстрее" на P5, потому что он все равно не мог быть спекулятивным или неупорядоченным, и выполнение инструкций x86 с физическими адресами (подкачка отключена, чтобы избежать перехвата-22) позволило бы кэширование загрузок таблицы страниц. Возможно, Энди думал о MIPS, которая в то время была его дневной работой.


Больше от Энди Глеу из той же темы, потому что эти комментарии заслуживают полного ответа где-то.

(2) одно из моих самых больших сожалений по поводу P6 заключается в том, что мы не предоставили поддержку согласованности TLB внутри инструкции. Некоторые инструкции обращаются к одной и той же странице более одного раза. Разные мопы в одной и той же инструкции могли получать разные переводы для одного и того же адреса. Если бы мы дали микрокоду возможность сохранять преобразование физического адреса, а затем использовать его, имхо было бы лучше.

(2a) Когда я присоединился к P6, я был сторонником RISC, и моя позиция была такова: "пусть SW (микрокод) сделает это".

(2a ') одна из самых смущающих ошибок была связана с дополнением-переносом памяти. В начале микрокод. Загрузка будет выполняться, флаг переноса будет обновлен, и хранилище может вызвать ошибку -but, флаг переноса уже обновлен, поэтому инструкция не может быть перезапущена.//это было простое исправление микрокода, сохранение хранилища до того, как был написан флаг переноса, но одного лишнего мопа было достаточно, чтобы эта инструкция не вписывалась в систему кодирования со средней скоростью.

(3) В любом случае - основная "поддержка" P6 и ее потомков, оказанная для решения проблем когерентности TLB, заключалась в том, чтобы пересмотреть таблицы страниц при выходе на пенсию, прежде чем сообщать об ошибке. Это позволило избежать путаницы с ОС, сообщая о сбое, когда в таблицах страниц указано, что его не должно быть.

(4) мета-комментарий: я не думаю, что какая-либо архитектура имеет правильно определенные правила для кэширования недопустимых записей TLB.//AFAIK большинство процессоров не кэшируют недопустимые записи TLB - за исключением, возможно, Itanium с его страницами NAT (Not A Thing). Но есть реальная необходимость: спекулятивный доступ к памяти может быть по диким адресам, пропускать TLB, обходить дорогостоящие таблицы страниц, замедлять выполнение других инструкций и потоков - и затем делать это снова и снова, потому что тот факт, что "это плохо адрес, не нужно ходить по страницам таблицы "не запоминается.//Я подозреваю, что DOS-атаки могут использовать это.

(4 ') хуже того, ОС могут делать неявные предположения о том, что недопустимые трансляции никогда не кэшируются, и, следовательно, не делать недействительными TLB или сбрасывать MP TLB при переходе от недействительного к действительному.//Хуже ^ 2: представьте, что вы кэшируете внутренние узлы кэша таблицы страниц. Представьте, что PD содержит все недействительные PDE; хуже того, 3: PD содержит действительные d PDE, которые указывают на недействительные PT. Вам все еще разрешено кэшировать эти PDE? Когда именно ОС должна сделать запись недействительной?

(4 ''), поскольку сбой MP TLB с использованием межпроцессорных прерываний был дорогим, парни из-за производительности ОС (как я раньше) всегда приводят аргументы типа "нам не нужно аннулировать TLB после изменения PTE с недействительного на действительный" или "от действительного только для чтения к действительному доступному для записи с другим адресом". Или "нам не нужно аннулировать TLB после изменения PDE, чтобы указать на другой PT, чьи PTE точно такие же, как исходный PT...".//Множество гениальных аргументов. К сожалению, не всегда правильно.

Некоторые из моих друзей-компьютерных архитекторов теперь поддерживают согласованные TLB: TLB, которые отслеживают запись, как кеширование данных. Главным образом, чтобы позволить нам создавать еще более агрессивные TLB и кэши таблиц страниц, если допустимы и недействительные записи конечных и внутренних узлов. И не нужно беспокоиться о предположениях парней из ОС.//Я еще не там: слишком дорого для недорогого оборудования. Но, возможно, стоит заняться на высоком уровне.

я: Черт возьми, так откуда же взялись эти дополнительные ALU uop в АЦП памяти, даже на семействах Core2 и SnB? Никогда бы не догадался, но был озадачен этим.

Энди: часто, когда вы "делаете RISC", требуются дополнительные инструкции или микро-инструкции в тщательном порядке. Принимая во внимание, что если у вас есть поддержка CISCy, например, поддержка специального оборудования, так что одна инструкция является транзакцией, либо выполненной, либо невыполненной, можно использовать более короткие последовательности кода.

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

То же самое упорядочение памяти. Верхний конец выслеживает быстрее; нижний конец сливает дешевле.

Трудно поддерживать эту дихотомию.

Весьма распространено, что конкретная реализация должна реализовывать правила, совместимые, но более строгие, чем архитектурное утверждение. Но не все реализации должны делать это одинаково.

Эта ветка комментариев была о том, как Энди ответил на вопрос о самом изменяющемся коде и просмотре устаревших инструкций; другой случай, когда реальные процессоры выходят за рамки требований, изложенных на бумаге, потому что на самом деле легче всегда отслеживать магазины рядом с EIP/RIP, чем выполнять повторную синхронизацию только по инструкциям ветвей, если вы не отслеживали то, что происходило между ветвями.