Вопрос по c++, c – Выровненные и не выровненные обращения к памяти?

14

В чем разница между выровненным и не выровненным доступом к памяти?

Я работаю на DSP TMS320C64x, и я хочу использовать встроенные функции (функции C для инструкций по сборке), и он имеет

ushort & _amem2(void *ptr);
ushort & _mem2(void *ptr);

где_amem2 выровненный доступ 2 байта и_mem2 делает невыровненный доступ.

Когда я должен использовать что?

Ваш Ответ

6   ответов
16

х байтов каждый. Например, в 32-разрядной архитектуре Intel хранятся слова размером 32 бита, каждое из которых содержит 4 байта. Однако память адресована на уровне одного байта; поэтому адрес может быть «выровненным», то есть начинаться с границы слова, или «не выровненным», то есть он не имеет значения.

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

Итак, если вы знаете, что ваши адреса выровнены по правильным адресам, вы можете использовать _amem2 () для скорости. В противном случае вы должны использовать _mem2 ().

Error: User Rate Limit Exceeded
3

кратные рассматриваемому размеру доступа.

Access of 4 byte words on addresses that are multiple of 4 will be aligned Access of 4 bytes from the address (say) 3 will be unaligned access

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

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

Когда дело доходит до 2-х байтовых обращений, определить выровненные операции очень просто.
Если все адреса доступа для операции являются «четными» (то есть их LSB равен нулю), у вас есть 2-байтовое выравнивание. Это можно легко проверить с помощью

if (address & 1) // is true
    /* we have an odd address; not aligned */
else
    /* we have an even address; its aligned to 2-bytes */
3

_mem2 является более общим.. _amem2 более строг: он требует, чтобы ptr был выровнен (хотя, вероятно, немного более эффективен). Так что используйте _mem2, если вы не можете гарантировать, что ptr всегда выровнен.

Error: User Rate Limit Exceeded Can Bal
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
3

ый доступ генерирует прерывание исключения (например, ARM), или просто медленнее (например, x86).

_mem2 вероятно, реализован как выборка двух байтов и использование сдвиговых и / или побитовых операций для создания из них 16-битного короткого текста.

_amem2 вероятно, просто читает 16-битный ushort из указанного ptr.

Я не знаю TMS320C64x конкретно, но полагаю, что для 16-битного доступа к памяти требуется 16-битное выравнивание. Так что вы можете использовать_mem2 всегда, но со снижением производительности и_amem2 когда вы можете гарантировать, что ptr является четным адресом.

15

что указатель (как целое число) кратен типозависимому значению, называемому выравниванием. Выравнивание является естественным адресом, кратным типу, где тип должен быть или должен быть сохранен (например, из соображений производительности) в CPU. Например, ЦП может потребовать, чтобы все двухбайтовые загрузки или сохранения выполнялись по адресам, кратным двум. Для небольших примитивных типов (до 4 байтов) выравнивание почти всегда соответствует размеру типа. Для структур выравнивание обычно является максимальным выравниванием любого элемента.

Компилятор C всегда помещает переменные, которые вы объявляете по адресам, которые удовлетворяют & quot; правильному & quot; выравнивание. Так что, если ptr указывает, например, на переменная uint16_t, она будет выровнена, и вы можете использовать _amem2. Вы должны использовать _mem2, только если вы открываете, например, упакованный байтовый массив, полученный через ввод / вывод, или байты в середине строки.

2

что это старый вопрос с выбранным ответом, но не видел, чтобы кто-нибудь объяснил ответ на вопрос, в чем разница между выравниванием и выравниванием доступа к памяти ...

Будь то драма или срам, или вспышка, или другое. Возьмите sram в качестве простого примера, он построен из битов, определенный sram будет построен из фиксированного числа битов в ширину и фиксированного числа строк в глубину. скажем, 32 бита в ширину и несколько / много строк в глубину.

если я выполню 32-битную запись по адресу 0x0000 в этом sram, контроллер памяти вокруг этого sram может просто выполнить один цикл записи в строку 0.

если я выполню 32-битную запись по адресу 0x0001 в этом sram, при условии, что это разрешено, контроллеру потребуется выполнить чтение строки 0, изменить три байта, сохранив один, и записать это в строку 0, а затем прочитать строку 1 измените один байт, оставив остальные три, как найдено, и запишите его обратно. какие байты модифицируются или не имеют отношения к порядку байтов для системы.

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

Если бы я должен был прочитать 32 бита с адреса 0x0000, то было бы выполнено одно чтение строки 0. Но читайте с 0x0001, и мне нужно сделать два чтения row0 и row1, и в зависимости от конструкции системы просто отправить эти 64 бита обратно в процессор, возможно, на две тактовые частоты шины вместо одной. или контроллер памяти имеет дополнительную логику, чтобы 32 бита были выровнены на шине данных в одном цикле шины.

16-битное чтение немного лучше, чтение из 0x0000, 0x0001 и 0x0002 будет считываться только из row0 и может в зависимости от конструкции системы / процессора отослать эти 32 бита назад, а процессор извлечет их или сдвинуть в память контроллер, так что они приземляются на определенных дорожках байтов, так что процессор не должен вращаться вокруг. Один или другой должен, если не оба. Чтение из 0x0003, как и выше, требует чтения строки 0 и строки 1, поскольку один из ваших байтов находится в каждом, а затем либо отправляет 64 бита назад для извлечения процессором, либо контроллер памяти объединяет биты в один 32-битный ответ шины ( предполагая, что шина между процессором и контроллером памяти имеет ширину 32 бита для этих примеров).

16-битная запись всегда заканчивается как минимум одним чтением-изменением-записью в этом примере sram, адреса 0x0000, 0x0001 и 0x0002, чтение строки0, изменение двух байтов и запись назад. По адресу 0x0003 читаем две строки, модифицируем по одному байту и записываем обратно.

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

Armv4 не понравился как unaligned, хотя вы могли отключить ловушку, и результат не такой, как вы ожидаете выше, что не важно, текущее вооружение разрешает unaligned и дает вам вышеупомянутое поведение, которое вы можете немного изменить в контрольном регистре, и затем оно будет отменять unaligned переводы. Mips раньше не позволяли, не уверен, что они делают сейчас. x86, 68K и т. д. были разрешены, и контроллеру памяти, возможно, приходилось выполнять большую часть работы.

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

Я не согласен с тем, что компилятор всегда выравнивает данные прямо для цели, есть способы сломать это. И если цель не поддерживает выравнивание, вы попадете на ошибку. Программистам никогда не нужно было бы говорить об этом, если бы компилятор всегда делал это правильно, основываясь на любом легальном коде, который вы могли бы придумать, для этого вопроса не было бы никаких причин, если бы он не был связан с производительностью. если вы не управляете адресом void ptr для выравнивания или нет, то вы должны все время использовать доступ без выравнивания mem2 () или делать в коде if-then-else, основываясь на значении ptr как nik указал. объявив void, компилятор C теперь не может правильно обработать ваше выравнивание, и это не будет гарантировано. если вы возьмете char * prt и передадите его этим функциям, все ставки на компиляторе отключены, если вы не добавите дополнительный код, либо скрытый в функции mem2 (), либо вне этих двух функций. так как написано в вашем вопросе mem2 () является единственным правильным ответом.

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

Похожие вопросы