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

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

Я не уверен, происходит ли "обход страниц" в специальных аппаратных схемах, или таблицы таблиц хранятся в кэше 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 внутри аппаратного обеспечения просмотра страниц еще более ценным, поскольку один PDE охватывает много разных записей перевода.

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

(Историческая справка: ответ Энди Глью на дубликат этого вопроса об электронике.SE говорит, что в P5 и более ранних версиях аппаратные загрузки страниц обходили кеш. Он также подтверждает, что P6 и более поздние версии загружаются из обычного кеша L1. Другой ответ на этот вопрос посвящен устранению ошибок TLB в программном обеспечении, когда аппаратный обходчик страниц отключен, и вместо этого он переходит в ловушку для ОС, такой как MIPS (см. ниже), которая, по-видимому, была быстрее на P5. в конце, включая статью, на которую я ссылался в конце последнего абзаца.)


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

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

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

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


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

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

Как упоминалось ранее, управление SW TLB является опцией на x86, если вы отключили функцию просмотра страниц HW, и это было победой на P5.

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


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

Основной причиной, по которой Intel начала выполнять обход таблицы кеша, а не обходить кеш, была производительность. До P6 страницы обходились медленно, не извлекали выгоду из кеша и не были спекулятивными. Достаточно медленно, чтобы программная обработка ошибок TLB была выигрышем в производительности. 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, которые не существовали в то время.


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

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

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

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

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

(4) мета-комментарий: я не думаю, что какая-либо архитектура имеет правильно определенные правила для кэширования недопустимых записей TLB. // AFAIK большинство процессоров не кэшируют недопустимые записи TLB - за исключением, возможно, Itanium с его страницами NAT (не 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, чем выполнять повторную синхронизацию только по инструкциям ветвей, если вы не отслеживали то, что происходило между ветвями.

Другие вопросы по тегам