Выровненные и несогласованные доступы к памяти?

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

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

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

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

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

6 ответов


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

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


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

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

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


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


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

_mem2 вероятно выполнены как два байта выборки и с помощью операций сдвига и поразрядные сделать 16-битный ushort из них.

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

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


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

  • доступ 4 байтовых слов по адресам, кратным 4, будет выровнен
  • доступ 4 байт от адреса (скажем) 3 будет unaligned access

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

поэтому, когда вам нужна производительность (особенно когда вы знаете, что задержка доступа высока), было бы разумно определить, когда вы можете использовать выровненный доступ. The _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 */

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

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

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

Если я буду делать 32-битный напишите адрес 0х0001 в ОЗУ, при условии, что это разрешено, контроллер нужно читать строки 0, измените три байта, сохраняя одно и написать, что в строке 0, то читать подряд 1. изменить один байт, оставляя три других, как найти и записать обратно. какие байты изменяются или не имеют отношения к endianness для системы.

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

Если бы я должен был прочитать 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 read row0 измените два байта и напишите обратно. адрес 0x0003 прочитайте две строки, измените по одному байту и запишите обратно.

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

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

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

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

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