Как современный cpus управляет перекрестным доступом без привязки?

Я пытаюсь понять, как unaligned memory access (UMA) работает на современных процессорах (а именно x86-64 и arm archures). Я получаю, что у меня могут возникнуть проблемы с UMA, начиная с дефодии префранции до отказа процессора. И я читал о posix_memalign и строках кэша.

Что я не могу найти, так это то, как современные системы/аппаратные средства обрабатывают ситуацию, когда мой запрос превышает границы страниц?

Вот пример:
1) Я malloc() блок памяти объемом 8 КБ.
2) Скажем, что malloc() не хватает памяти и sbrk() s 8KB для меня.
3) Ядро получает две страницы памяти (по 4 Кб каждая) и сопоставляет их в пространстве виртуального адреса процесса (скажем, что эти две страницы не одно за другим в памяти

4) movq (смещение + $0xffc),% rax; Я запрашиваю 8 байтов, начиная с 4092-го байта, что означает, что я хочу 4 байта от конца первой страницы и 4 байта от начала второй страницы.

физическая память:

---|---------------|---------------|-->
   |... 4b|        |        |4b ...|-->

Мне нужно 8 байтов, разделенных на границах страницы.

Как работает MMU на x86-64 и руке? Существуют ли какие-либо механизмы в ядре mm, чтобы как-то подготовиться к подобному запросу? Есть ли какая-то защита в malloc? Что делают процессоры? Получают ли они две страницы?

Я хочу завершить такой запрос. MMU должен перевести один виртуальный адрес на два физических адреса. Как он обрабатывает такой запрос?

Я должен заботиться о таких вещах, если я программист и почему?

Я читаю много ссылок из google, SO, drepper cpumemory.pdf и gorman linux vmm book на данный момент. Но это океан информации. Было бы здорово, если бы вы хотя бы предоставили мне некоторые указатели или ключевые слова, которые я мог бы использовать.

Спасибо.

Ответы

Ответ 1

Я не слишком разбираюсь в смелости архитектуры Intel, но архитектура ARM суммирует эту конкретную деталь в единственной точке в подпункте "Ограниченные ограничения доступа к данным":

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

Таким образом, кроме возможности генерировать две ошибки страницы из одной операции, это просто еще один непривязанный доступ. Конечно, это все еще предполагает все предостережения "просто еще одного неприглаженного доступа", а именно: он действителен только для нормальной (не аппаратной) памяти, только для определенных инструкций загрузки/хранения, не имеет гарантии атомарности и может быть медленным - микроархитектура будет скорее всего, синтезирует неравнозначный доступ из нескольких согласованных доступов 1 что означает несколько переводов MMU, возможно, много промахов в кеше, если оно пересекает границу линии и т.д.

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

Я быстро просмотрел руководства Intel, и их ответ не выскочил на меня, однако в главе "Типы данных" они описывают:

[...] процессор требует двух обращений к памяти, чтобы сделать несвязанный доступ; выровненный доступ требует только одного доступа к памяти.

поэтому я был бы удивлен, если бы не был в целом одинаковым (т.е. один перевод на один доступ).

Теперь, это то, на что программистам на уровне приложений не приходится беспокоиться, если они ведут себя сами - вне языка ассемблера, на самом деле довольно сложно сделать несвязанные обращения. Вероятные виновники - это стрелки, указывающие на тип, и возиться со структурой упаковки, обе вещи, в 99% случаев у которых нет причин приближаться, а для остальных 1% все еще почти наверняка не так.


[1] Псевдокод архитектуры ARM на самом деле указывает неприглаженные обращения как ряды отдельных байтовых доступов, но я ожидаю, что реализации фактически оптимизируют это на более крупные согласованные обращения, где это необходимо.

Ответ 2

Таким образом, архитектура не имеет особого значения, кроме того, что x86 традиционно напрямую не рассказывал вам не о том, где mips и arm традиционно генерируют прерывание данных, а не пытаются просто заставить его работать.

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

Переводы обычно находятся в некоторых единицах байтов и/или шинах. Например, на шине ARM amba/axi поле длины находится в единицах ширины шины, 32 или 64 бита, 4 или 8 байтов. И нет, это не будет в единицах 4 Кбайта....

(да, это элементарно, я полагаю, вы все это понимаете).

Является ли это 16 бит или 128 бит, штраф за неуравнимый исходит из дополнительных циклов шины, которые в эти дни являются дополнительными шинами. Таким образом, для 16-битной неравномерной передачи ARM (какая рука будет поддерживать на своих новых ядрах без сбоев), что означает, что вам нужно читать 128 бит вместо 64, 64 бит, чтобы получить 16, не является штрафом, поскольку 64 - это самый маленький размер для шины перевод. Каждая передача, будь то единая ширина шины данных или несколько, имеет несколько связанных с ней тактовых циклов, позволяет сказать, что имеется 6 тактов, чтобы выполнить согласованное 16-битное чтение, тогда в идеале это 7 циклов, чтобы выполнить неравновесный 16 бит. Кажется маленьким, но это все складывается.

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

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

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

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

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

Я также больше всего знаком с ARM. Рука традиционно наказывала неприсоединенный доступ с прерыванием, вы могли бы отключить это, и вместо этого вы могли бы получить ротацию шины, а не проливать ее, что могло бы привести к некоторым приятным котировкам. более современные сердечники рук будут терпеть и осуществлять неравномерную передачу. Понимать, например, несколько магазинов, например 4 или более регистров, по адресу, не адресуемому 64-битной, не считаются неравномерным доступом, хотя он является 128-битной записью на адрес, который не соответствует ни 64, ни 128 бит. То, что процессор делает в этом случае, тормозит его на 3 записи, выровненную 32-разрядную запись, выровненную 64-разрядную запись и выровненную 32-разрядную запись. контроллеру памяти не приходится иметь дело с неуравновешенным материалом. Это касается юридических вещей, таких как множество магазинов. я знаю, что я обычно не знаю, как писать длину более 2, а 8-битное хранилище с несколькими регистрами - это не одна длина записи 4, это 2 отдельных длины двух записей. Но нагрузка, кратная 8 регистрам, так что она выровнена по 64-битовому адресу - это одна длина 4 транзакции. Я уверен, что, поскольку для чтения нет маскировки на стороне шины, все находится в единицах ширины шины, нет оснований для разрыва, скажем, 4-разрядная загрузка нагрузки по адресу, которая не равна 64 битам, в 3 транзакции, просто выполните 3 чтения. Когда процессор читает один байт, вы не можете сказать, что из шины все, что вы видите, - это 64-битное чтение AFAIK. Процессор разбивает байтовую полосу. Если процессор/шина позаботится о том, чтобы это рука, x86, mips и т.д., То вы наверняка увидите отдельные передачи.

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

Пример вашего malloc. Во-первых, вы не увидите передачи одной шины в 4 Кбайта, что в любом случае 4k будет разбито на удобочитаемые биты. во-первых, для того, чтобы найти физический адрес и другие свойства в любом случае, он должен сделать один для многих циклов шины, чтобы найти физический адрес и другие свойства (эти ответы могут быть кэшированы, чтобы сделать их быстрее, но иногда они должны пройти весь путь, чтобы замедлить драм) поэтому для этого примера единственная передача, которая имеет значение, - это выровненная передача, которая разбивает границу 4k, например 16-разрядную передачу, для того, чтобы система mmu работала единственным способом для поддержки, которая должна быть превращена в два отдельных 8-битные передачи, которые происходят в этих физических адресных пространствах, и да, что буквально удваивает все, что просматривает mmu, циклически циклирует циклы кэша/драма и т.д. Помимо этой границы нет ничего особенного в том, что ваш 8k разделен. основная часть ваших циклов будет находиться в пределах одной из двух страниц 4k, поэтому он выглядит как любой другой случайный доступ, причем, конечно, повторяющиеся/последовательные обращения получают преимущество кэширования.

Короткий ответ заключается в том, что независимо от того, на какой платформе вы находитесь: 1) платформа прервет несвязанный перевод, или 2) где-то на пути есть еще один или несколько (десятки/сотни) в результате unaligned доступ по сравнению с выровненным доступом.

Ответ 3

Не имеет значения, смежны ли физические страницы или нет. Современные процессоры используют кеши. Данные передаются в/из DRAM по полной кеш-строке за раз. Таким образом, DRAM никогда не увидит многобайтового чтения или записи, который пересекает границу 64B, не говоря уже о границе страницы.

Магазины, пересекающие границу страницы, все еще медленны (на современном x86). Я предполагаю, что аппаратное обеспечение обрабатывает случай с разбивкой по страницам, обнаружив его на более поздней стадии конвейера и инициируя повторное выполнение, которое выполняет две проверки TLB. IDK, если проекты Intel вставляют лишние шаги в конвейер для обработки или что. (т.е. влияние на латентность, пропускную способность разделов страниц, пропускную способность всех обращений к памяти, пропускную способность других (например, не-памяти) uops).

Как правило, нет никакого штрафа за неуспешные обращения в пределах строки кэша (начиная с Nehalem) и небольшой штраф за расщепления строк кэша, которые не разделяются на страницы. Четный раскол, по-видимому, дешевле других. (например, нагрузка 16B, которая берет 8B от одной строки кэша и 8B от другой).

Во всяком случае, DRAM никогда не увидит неприглаженный доступ напрямую. AFAIK, нет здравомыслящего современного дизайна, имеет только кэши с записью, поэтому DRAM видит только записи, когда кэш-строка очищается, и в этот момент недоступен один неприглаженный доступ, загрязненный двумя линиями кэша. Кэши даже не записывают, какие байты загрязнены; они просто всплескивают - записывают весь 64B на следующий уровень вниз (или последний уровень в DRAM), когда это необходимо.

Есть, вероятно, некоторые конструкции процессоров, которые не работают таким образом, но и проекты Intel и AMD также таковы.


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

Uncacheable unalached stores, которые пересекают границу страницы, вероятно, по-прежнему разделены на отдельные магазины (поскольку каждая часть нуждается в отдельном переводе TLB).


Предостережение 2: Я не проверял факт. Я уверен, что общий доступ к DRAM для "нормальных" нагрузок/хранилищ в "нормальных" областях памяти поддерживается для всех кеш-строк.