Насколько я понимаю, если память выровнена, код будет выполняться быстрее, потому что процессору не придется делать дополнительный шаг, чтобы восстановить вырезанные биты памяти.
Это не обязательно вещь исполнения, x86 имеет инструкции переменной длины, начинающиеся с одиночных 8-битных инструкций, вплоть до нескольких байтов, и все о том, что они не выровнены. но они предприняли меры, чтобы сгладить это по большей части.
Если у меня 64-битная шина на краю моего процессора, это не означает край чипа, который означает край ядра. Другая сторона этого - контроллер памяти, который знает протокол шины и является первым местом, где адреса начинают декодироваться, и транзакции начинают разделять другие шины к их месту назначения.
Это очень большая архитектура и спецификация конструкции шины c, и вы можете иметь архитектуры с разными шинами с течением времени или разными версиями, например, вы можете получить руку с шиной 64 или 32 бита. но допустим, что у нас неоптическая ситуация, когда шина имеет ширину 64 бита и все транзакции на этой шине выровнены на границе 64 бита.
Если бы я должен был выполнить 64-битную запись в 0x1000, которая была бы транзакцией с одной шиной, которая в наши дни является своего рода шиной адреса записи с некоторым идентификатором x и длиной 0 (n-1), то другая сторона подтверждает, что я вижу, что вы хотите сделать запись с идентификатором x, я готов принять ваши данные. Затем процессор использует шину данных с идентификатором x для отправки данных, один такт на 64 бита, это один 64-битный, поэтому один такт на этой шине. и, возможно, подтверждение возвращается, а может и нет.
Но если бы я захотел выполнить 64-битную запись в 0x1004, то получилось бы, что бы это превратилось в две транзакции - одну полную 64-битную транзакцию адрес / данные по адресу 0x1000 с только 4 байтовые дорожки позволили использовать дорожки 4-7 (представляющие байты по адресу 0x1004-0x1007). Затем завершена транзакция в 0x1008 с включенными 4-байтовыми дорожками, полосы 0-3. Таким образом, фактическое перемещение данных по шине происходит от одного часа до двух, но для достижения этих циклов данных также требуется в два раза больше рукопожатий. На этой шине очень заметно, какова общая конструкция системы, хотя вы можете чувствовать это или нет, или, возможно, придется сделать много из них, чтобы почувствовать это или нет. Но есть неэффективность, скрытая в шуме или нет.
Мне кажется, я понимаю, что 64-разрядный процессор читает 64-разрядную 64-разрядную память.
Не очень хорошее предположение на всех. В наши дни 32-битные ARM имеют 64-битные шины, например, ARMv6 и ARMv7 поставляются с ними или могут.
Теперь давайте представим, что у меня есть структура с порядком (без заполнения): char, короткий, символ и инт. Почему короткие будут смещены? У нас есть все данные в блоке! Почему он должен быть на адресе, кратном 2. Один и тот же вопрос для целых чисел и других типов?
unsigned char a 0x1000
unsigned short b 0x1001
unsigned char c 0x1003
unsigned int d 0x1004
Вы обычно используете элементы структуры в коде что-то. что-то .b что-то. c что-то.d. Когда вы получаете доступ к нечто .b, это 16-битная транзакция по шине. В 64-битной системе вы правы в том, что если выровнен, как я к нему обращался, то вся структура читается, когда вы делаете x = что-то .b, но процессор отбрасывает все, кроме байтовых дорожек 1 и 2 (отбрасывая 0 и 3-7), то при доступе к чему-либо. c он выполнит другую транзакцию шины в 0x1000 и отбросит все, кроме полосы 3. Когда вы делаете запись во что-то. включен. Теперь, когда возникает больше боли, если есть кеш, он, вероятно, также построен из 64-битного ОЗУ для сопряжения с этой шиной, не обязательно, но давайте предположим, что это так. Вы хотите записать через кеш что-то. b, транзакция записи в 0x1000 с байтовыми дорожками 1 и 2 включена 0, 3-7 отключена. Кэш, в конечном счете, получает эту транзакцию, ему необходимо выполнить запись с изменением чтения, поскольку она не является полной 64-битной транзакцией (все линии включены), поэтому вы получаете удар с этой записью с изменением чтения с точки зрения производительности. (то же самое было верно для 64-битной записи без выравнивания выше).
короткое положение не выровнено, потому что когда упакован его адрес lsbit, для выравнивания 16-битный элемент в 8-битном мире байтов должен быть ноль, для выравнивания 32-битного элемента младшие два бита его адреса равны нулю, 64-битному, трем нулям и т. д.
в зависимости от системы, в которой вы можете оказаться на 32 или 16-битной шине (не столько для памяти, сколько в наши дни), так что вы можете получить возможность многократных переносов.
Ваши высокоэффективные процессоры, такие как MIPS и ARM, использовали подход выровненных команд и принудительно выровняли транзакции даже во что-то. В случае, если конкретно нет штрафа на 32 или 64-битной шине. Подход заключается в производительности по сравнению с потреблением памяти, поэтому инструкции в некоторой степени расточительны при их использовании, чтобы быть более эффективными при их извлечении и выполнении. Шина данных также намного проще. Когда создаются высокоуровневые концепции, такие как структура в C, происходит потеря памяти при заполнении каждого элемента в структуре для повышения производительности.
unsigned char a 0x1000
unsigned short b 0x1002
unsigned char c 0x1004
unsigned int d 0x1008
в качестве примера
У меня также есть второй вопрос: со структурой, о которой я упоминал ранее, как процессор узнает, когда он читает свои 64 бита, что первые 8 бит соответствуют символу, а следующие 16 соответствуют короткому et c .. .?
unsigned char c 0x1003
компилятор генерирует однобайтовое чтение по адресу 0x1003, это превращается в указанную инструкцию c с этим адресом, и процессор генерирует транзакцию шины для этого, Затем другая сторона процессорной шины выполняет свою работу и так далее.
Компилятор в общем случае не превращает упакованную версию этой структуры в одну 64-битную транзакцию, которая дает вам все элементы вы записываете 64-битную транзакцию шины для каждого элемента.
возможно, что в зависимости от набора команд, prefetcher, cac и т. д. и т. д. вместо того, чтобы использовать структуру на высоком уровне, вы создаете одно 64-битное целое число и выполняете работу в коде, тогда вы можете повысить или не повысить производительность. Ожидается, что это не будет работать лучше на большинстве архитектур, работающих с кешами и тому подобным, но когда вы попадаете во встроенные системы, где у вас может быть некоторое количество состояний ожидания на оперативной памяти или некоторое количество состояний ожидания на флаге sh или любого другого кода В хранилище вы можете найти время, когда вместо меньшего количества инструкций и большего количества транзакций данных вам нужно больше инструкций и меньше транзакций данных. Код является линейным фрагментом кода, таким как чтение, маска и смещение, маска и смещение и т. д. хранилище команд может иметь пакетный режим для линейных транзакций, но транзакции данных занимают столько тактов, сколько они берут.
Промежуточная точка - просто сделать все 32-битной переменной или 64-битной, тогда все выровнено и относительно неплохо работает за счет увеличения используемой памяти.
Поскольку люди не понимают выравнивание, были испорчены программированием x86, решили использовать структуры в доменах компиляции (такая плохая идея), ARM и другие Допустив невыровненный доступ, вы можете ощутить снижение производительности на этих платформах, поскольку они настолько эффективны, если все выровнено, но когда вы делаете что-то не выровненное, оно просто генерирует больше шинных транзакций, делая все дольше. Таким образом, старые руки будут по умолчанию давать сбой, рука 7 может отключить ошибку, но будет вращать данные вокруг слова (хороший прием для замены 16-битных значений в слове), а не перетекать в следующее слово, более поздние архитектуры по умолчанию не ошибка на выровненных, или большинство людей устанавливает их не на ошибки на выровненных, и они читают / записывают невыровненные передачи, как можно было бы надеяться / ожидать.
Для каждого чипа x86, установленного на вашем компьютере, у вас есть несколько, если не горстка процессоров, отличных от x86, в том же компьютере или периферийных устройствах, свисающих с этого компьютера (мышь, клавиатура, монитор и т. Д. c). Многие из них - 8-битные 8051 и z80, но многие из них основаны на использовании рук. Таким образом, существует множество разработок, не связанных с x86, и не только на основных процессорах телефонов и планшетов. Этим другим нужны низкая стоимость и низкое энергопотребление, чтобы повысить эффективность кодирования как по производительности шины, так и по тактовой частоте, но и по общему балансу использования кода / данных для снижения стоимости флэш-памяти / оперативной памяти.
Довольно сложно заставить эти проблемы с выравниванием на платформе x86, есть много накладных расходов для преодоления ее архитектурных проблем. Но вы можете увидеть это на более эффективных платформах. Это как поезд против спортивной машины, что-то падает с поезда, с которого человек спрыгивает или набирает обороты, его не замечают ни капли, но шаг измените массу на спортивной машине, и вы почувствуете это. Поэтому, пытаясь сделать это на x86, вам придется работать намного усерднее, если вы даже сможете понять, как это сделать. Но на других платформах легче увидеть эффект. Если вы не найдете чип 8086, и я подозреваю, что вы можете почувствовать разницу, придется вытащить мое руководство для подтверждения.
Если вам посчастливилось получить доступ к источникам / симуляциям микросхем, вы можете увидеть, что подобные вещи происходят повсюду, и действительно можете приступить к ручной настройке вашей программы (для этой платформы). Аналогичным образом вы можете увидеть, что кэширование, буферизация записи, предварительная выборка команд в различных формах и т. Д. Влияют на общую производительность и порой создают параллельные периоды времени, когда могут скрываться другие неэффективные транзакции, или создаются намеренные резервные циклы, чтобы что транзакции, которые занимают дополнительное время, могут иметь временной интервал.