Почему гранулярность адресного пространства составляет 64К?


Перевод с блога The Old New Thing. Оригинал здесь.

Возможно, вам любопытно, почему VirtualAlloc выделяет память по границам 64К, хотя страничная гранулярность равна 4К.

За это следует благодарить процессор Alpha AXP.

На этом процессоре не существует команды "загрузить 32-битное целое значение". Для того, что сделать это, необходимо загрузить два 16-битный целых значения и объединить их.

Таким образом, если бы гранулярность была точнее, чем 64К, то DLL библиотека, загруженная не по предполагаемому адресу, потребовала бы двух операций при подстройке каждого адреса: одну для страших 16 бит и одну для младших.

Все обстоит гораздо хуже, если в результате этого происходит перенос или заем между двумя половинами. (Например, для изменения адреса на 4К с 0х1234F000 на 0х12350000 требуется изменить и старшую и младшую части адреса. Даже притом, что величина изменения намного меньше 64К, оно тем не менее влияет и на старшую часть из-за переноса.)

Но и это еще не все.

Alpha AXP на самом деле складывает два знаковых 16-битных целых значения, чтобы получить 32-битное целое. Так, для того чтобы загрузить значение 0х1234ABCD, вы должны вначале использовать команду LDAH, чтобы загрузить значение 0х1235 в старшее слово нужного регистра. А затем при помощи команды LDA необходимо к нему прибавить знаковое значение -0x5433. (Поскольку 0х5433 = 0х10000 — 0хABCD.) Результатом будет требуемое значение 0х1234ABCD.

LDAH t1, 0x1235(ноль) // t1 = 0x12350000
LDA t1, -0x5433(t1)       // t1 = t1 — 0x5433 = 0x1234ABCD

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

Более того, компилятор — парень хитрый и если ему потребуется вычислить адреса двух переменных, находящихся в одной 64К области, то он использует только одну команду LDAH для этого. Если бы было возможно перемещение на величину, не кратную 64К, то компилятор уже не смог бы использовать такую оптимизацию, потому что после перемещения эти переменные могли находиться в разных 64К блоках.

Гранулярность памяти в 64К устраняет все эти проблемы.

Если Вы были очень внимательны, то Вы должны были заметить, что это также дает ответ на вопрос, почему существуют "запретные земли" около границ 2 гигабайтов. Рассмотрим, как формируется значение 0x7FFFABCD: Так как младшие 16 бит находятся в верхней половине 64-килобайтного диапазона, то итоговое значение должно быть сформировано при помощи вычитания, а не сложения. Очевидное решение такое

LDAH t1, 0x8000(ноль)  // t1 = 0x80000000, так?
LDA t1, -0x5433(t1)        // t1 = t1 — 0x5433 = 0x7FFFABCD, верно?

Верно, за тем исключением, что это не работает. Alpha AXP — это 64 битный процессор, а 0х8000 не попадает в 16-битное знаковое целое, поэтому приходится использовать -0х8000, отрицательное число. Что на самом деле означает:

LDAH t1, -0x8000(ноль) // t1 = 0xFFFFFFFF`80000000
LDA t1, -0x5433(t1)        // t1 = t1 — 0x5433 = 0xFFFFFFFF`7FFFABCD

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

ADDL t1, zero, t1           // t1 = t1 + 0, с суффиксом L
                                      // Суффикс L означает знаковое расширение результата с 32 бит до 64
                                      // t1 = 0x00000000`7FFFABCD

Если бы можно было использовать адреса из 64К окрестностей границ 2-х гигабайт, тогда каждое формирование адреса в памяти должно было включать эту третью ADDL инструкцию на тот случай, если адрес попадал в эту "опасную зону" около границ 2-х гигабайт.

Это была бы чрезвычайно высокая цена за доступ к последним 64К памяти адресного пространства (50% снижение производительности для всех вычислений адресов, чтобы защититься от случая, который практически не случается), таким образом, обозначение этой области как недоступной является вполне разумным решением.

Реклама
Запись опубликована в рубрике память. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s