Что такое выравнивание стека

Что такое «выравнивание стека»?

Что такое выравнивание стека? Почему он используется? Можно ли это контролировать с помощью настроек компилятора?

Подробности этого вопроса взяты из проблемы, возникающей при попытке использовать библиотеки ffmpeg с msvc, однако то, что меня действительно интересует, является объяснением того, что такое «выравнивание стека».

ОТВЕТЫ

Ответ 1

Выравнивание переменных в памяти (короткая история).

На прошлых компьютерах была 8-битная шина данных. Это означает, что каждый такт 8 бит информации может быть обработан. Тогда это было хорошо.

Затем появились 16-разрядные компьютеры. Из-за нисходящей совместимости и других проблем был сохранен 8-битный байт, и было введено 16-битное слово. Каждое слово было 2 байта. И каждый такт 16 бит информации может быть обработан. Но это создавало небольшую проблему.

Посмотрим на карту памяти:

В каждом адресе есть байт, доступ к которому возможен индивидуально. Но слова могут быть получены только по четным адресам. Поэтому, если мы читаем слово в 0000, мы читаем байты в 0000 и 0001. Но если мы хотим прочитать слово в позиции 0001, нам нужны два обращения к чтению. Сначала 00000001, а затем 0002 0003, и мы сохраняем только 0001 0002.

Конечно, это заняло некоторое дополнительное время, и это не было оценено. Вот почему они изобрели выравнивание. Таким образом, мы сохраняем переменные слов в границах слов и байтовых переменных на границах байтов.

Например, если у нас есть структура с байтовым полем (B) и полем слов (W) (и очень наивным компилятором), мы получаем следующее:

Это не весело. Но при использовании выравнивания слов мы находим:

Здесь память приносится в жертву за скорость доступа.

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

Ответ 2

Это означает, что если вы используете переменную, которая равна

Ответ 3

Некоторые архитектуры процессоров требуют конкретного выравнивания различных типов данных и будут генерировать исключения, если вы не соблюдаете это правило. В стандартном режиме x86 не требует этого для базовых типов данных, но может пострадать от штрафов за производительность (см. Www.agner.org для советов по оптимизации на низком уровне).

EDIT : что касается исключения, процедура в DLL, вероятно, хочет использовать инструкции SSE для некоторых временных данных стека и терпит неудачу, потому что два разных компилятора не согласны с вызовами конвенций.

Источник

Что такое «выравнивание стека»?

Что такое выравнивание стека? Почему он используется? Можно ли это контролировать с помощью настроек компилятора?

Подробности этого вопроса взяты из проблемы, возникающей при попытке использовать библиотеки ffmpeg с msvc, однако то, что меня действительно интересует, является объяснением того, что такое «выравнивание стека».

ОТВЕТЫ

Ответ 1

Выравнивание переменных в памяти (короткая история).

На прошлых компьютерах была 8-битная шина данных. Это означает, что каждый такт 8 бит информации может быть обработан. Тогда это было хорошо.

Затем появились 16-разрядные компьютеры. Из-за нисходящей совместимости и других проблем был сохранен 8-битный байт, и было введено 16-битное слово. Каждое слово было 2 байта. И каждый такт 16 бит информации может быть обработан. Но это создавало небольшую проблему.

Посмотрим на карту памяти:

В каждом адресе есть байт, доступ к которому возможен индивидуально. Но слова могут быть получены только по четным адресам. Поэтому, если мы читаем слово в 0000, мы читаем байты в 0000 и 0001. Но если мы хотим прочитать слово в позиции 0001, нам нужны два обращения к чтению. Сначала 00000001, а затем 0002 0003, и мы сохраняем только 0001 0002.

Конечно, это заняло некоторое дополнительное время, и это не было оценено. Вот почему они изобрели выравнивание. Таким образом, мы сохраняем переменные слов в границах слов и байтовых переменных на границах байтов.

Например, если у нас есть структура с байтовым полем (B) и полем слов (W) (и очень наивным компилятором), мы получаем следующее:

Это не весело. Но при использовании выравнивания слов мы находим:

Здесь память приносится в жертву за скорость доступа.

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

Ответ 2

Это означает, что если вы используете переменную, которая равна

Ответ 3

Некоторые архитектуры процессоров требуют конкретного выравнивания различных типов данных и будут генерировать исключения, если вы не соблюдаете это правило. В стандартном режиме x86 не требует этого для базовых типов данных, но может пострадать от штрафов за производительность (см. Www.agner.org для советов по оптимизации на низком уровне).

EDIT : что касается исключения, процедура в DLL, вероятно, хочет использовать инструкции SSE для некоторых временных данных стека и терпит неудачу, потому что два разных компилятора не согласны с вызовами конвенций.

Источник

Что значит выровнять стек?

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

далеко вниз учебник, Инструкции о том, как преобразовать Hello World! программа

в эквивалентный код сборки был дан и сгенерировано следующее:

для одного из линии,

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

6 ответов

предположим, что стек выглядит так при входе в _main (адрес указателя стека, это просто пример):

точки это то, что есть некоторые инструкции» SIMD «(одна инструкция, несколько данных) (также известные в x86-land как» SSE «для» Streaming SIMD Extensions»), которые могут выполнять параллельные операции с несколькими словами в памяти, но требуют, чтобы эти несколько слов были блоком, начинающимся с адреса, который кратен 16 байтам.

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

это не звучит как специфичный стек, но выравнивание в целом. Возможно, подумайте о термине integer multiple.

Если у вас есть элементы в памяти, которые имеют размер байта, единицы 1, то давайте просто скажем, что они все выровнены. Вещи, которые имеют размер два байта, а затем целые числа, умноженные на 2, будут выровнены, 0, 2, 4, 6, 8, etc. И нецелочисленные кратные, 1, 3, 5, 7 не будут выравнены. Элементы размером 4 байта, целочисленные кратные 0, 4, 8, 12 и т. д. выровнены, 1,2,3,5,6,7, и т. д. Не. То же самое относится к 8, 0,8,16,24 и 16 16,32,48,64, и так далее.

это означает, что вы можете посмотреть базовый адрес элемента и определить, выровнен ли он.

скажем, например, у вас есть два 8-байтовых элемента в стеке, всего 16 байтов, и вы действительно хотите, чтобы они были выровнены (на 8-байтовых границах). При входе функция вычитает 16 из указателя стека, как обычно, чтобы освободить место для этих двух элементов. Но чтобы выровнять их, нужно больше кода. Если мы захотим эти две 8-байтовые элементы выровнены на 8 байт границы и указатель стека после вычитания 16 были 0xFF82, ну, а нижние 3 бита не 0, поэтому он не выровнен. Нижние три бита 0b010. В общем смысле мы хотим вычесть 2 из 0xFF82, чтобы получить 0xFF80. Как мы определяем, что это 2 будет путем anding с 0b111 (0x7) и вычитания этой суммы. Это означает, что ALU операции и и вычитание. Но мы можем взять ярлык, если мы и с теми, которые дополняют значение 0x7 (

0x7 = 0xFFFF. FFF8) мы получаем 0xFF80, используя один alu операция (до тех пор, пока компилятор и процессор имеют один способ кода операции, чтобы сделать это, если нет, это может стоить вам больше, чем и и вычесть).

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

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

или если вы хотите просто взять if там и выполнять добавление и маску каждый раз.

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

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

существенной особенностью выравнивания байтов (при условии, что число равно 2) является то, что наименее значимое X бит адреса всегда равны нулю. Это позволяет процессору представлять больше адресов с меньшим количеством битов, просто не используя Нижний X бит.

представьте себе этот «чертеж»

значения по адресам, кратным 8 «слайд» легко в (64-разрядные) регистры

конечно регистрирует «прогулку» в шагах 8 байт

-16 это 11111111111111111111111111110000 в двоичном

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

когда процессор загружает данные из памяти в регистр, он должен получить доступ к базовым адресом и размером. Например, он будет получать 4 байта от адреса 10100100. Обратите внимание, что в конце этого примера есть два нуля. Это потому, что четыре байта хранятся так, что 101001 ведущие биты являются значимыми. (Процессор действительно обращается к ним через «все равно», получая 101001XX.)

так выровнять что-то в памяти значит переставить данные (обычно через padding), так что адрес нужного элемента будет иметь достаточно нулевых байтов. Продолжая приведенный выше пример, мы не можем получить 4 байта из 10100101, так как последние два бита не равны нулю; это вызовет ошибку шины. Таким образом, мы должны поднять адрес до 10101000 (и тратить три адресных местоположения в процессе).

компилятор делает это автоматически и представлен в коде сборки.

обратите внимание, что это проявляется как оптимизация в C / C++:

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

Источник

О стеке простыми словами — для студентов и просто начинающих

Привет, я студент второго курса технического университета. После пропуска нескольких пар программирования по состоянию здоровья, я столкнулся с непониманием таких тем, как «Стек» и «Очередь». Путем проб и ошибок, спустя несколько дней, до меня наконец дошло, что это такое и с чем это едят. Чтобы у вас понимание не заняло столько времени, в данной статье я расскажу о том что такое «Стек», каким образом и на каких примерах я понял что это такое. Если вам понравится, я напишу вторую часть, которая будет затрагивать уже такое понятие, как «Очередь»

Теория

На Википедии определение стека звучит так:

Стек (англ. stack — стопка; читается стэк) — абстрактный тип данных, представляющий собой список элементов, организованных по принципу LIFO (англ. last in — first out, «последним пришёл — первым вышел»).

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

Что такое выравнивание стека. Смотреть фото Что такое выравнивание стека. Смотреть картинку Что такое выравнивание стека. Картинка про Что такое выравнивание стека. Фото Что такое выравнивание стека

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

Итак, из чего же состоит стек.

Стек состоит из ячеек(в примере — это книги), которые представлены в виде структуры, содержащей какие-либо данные и указатель типа данной структуры на следующий элемент.
Сложно? Не беда, давайте разбираться.

Что такое выравнивание стека. Смотреть фото Что такое выравнивание стека. Смотреть картинку Что такое выравнивание стека. Картинка про Что такое выравнивание стека. Фото Что такое выравнивание стека

На данной картинке схематично изображен стек. Блок вида «Данные/*next» и есть наша ячейка. *next, как мы видим, указывает на следующий элемент, другими словами указатель *next хранит адрес следующей ячейки. Указатель *TOP указывает на вершину стек, то есть хранит её адрес.

С теорией закончили, перейдем к практике.

Практика

Для начала нам нужно создать структуру, которая будет являться нашей «ячейкой»

Новичкам возможно будет не понятно, зачем наш указатель — типа comp, точнее сказать указатель типа структуры comp. Объясню, для того чтобы указатель *next мог хранить структуру comp, ей нужно обозначить тип этой структуры. Другими словами указать, что будет хранить указатель.

После того как у нас задана «Ячейка», перейдем к созданию функций.

Функции

Функция создания «Стека»/добавления элемента в «Стек»

При добавлении элемента у нас возникнет две ситуации:

Разберем чуть чуть по-подробнее.
Во-первых, почему функция принимает **top, то есть указатель на указатель, для того чтобы вам было наиболее понятно, я оставлю рассмотрение этого вопроса на потом. Во-вторых, по-подробнее поговорим о q->next = *top и о том, что же означает ->.

-> означает то, что грубо говоря, мы заходим в нашу структуру и достаем оттуда элемент этой структуры. В строчке q->next = *top мы из нашей ячейки достаем указатель на следующий элемент *next и заменяем его на указатель, который указывает на вершину стека *top. Другими словами мы проводим связь, от нового элемента к вершине стека. Тут ничего сложного, все как с книгами. Новую книгу мы кладем ровно на вершину стопки, то есть проводим связь от новой книги к вершине стопки книг. После этого новая книга автоматически становится вершиной, так как стек не стопка книг, нам нужно указать, что новый элемент — вершина, для этого пишется: *top = q;.

Функция удаления элемента из «Стека» по данным

Данная функция будет удалять элемент из стека, если число Data ячейки(q->Data) будет равна числу, которое мы сами обозначим.

Здесь могут быть такие варианты:

Для лучшего понимания удаления элемента проведем аналогии с уже привычной стопкой книг. Если нам нужно убрать книгу сверху, мы её убираем, а книга под ней становится верхней. Тут то же самое, только в начале мы должны определить, что следующий элемент станет вершиной *top = q->next; и только потом удалить элемент free(q);

Если книга, которую нужно убрать находится между двумя книгами или между книгой и столом, предыдущая книга ляжет на следующую или на стол. Как мы уже поняли, книга у нас-это ячейка, а стол получается это NULL, то есть следующего элемента нет. Получается так же как с книгами, мы обозначаем, что предыдущая ячейка будет связана с последующей prev->next = q->next;, стоит отметить что prev->next может равняться как ячейке, так и нулю, в случае если q->next = NULL, то есть ячейки нет(книга ляжет на стол), после этого мы очищаем ячейку free(q).

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

Функция вывода данных стека на экран

Самая простая функция:

Здесь я думаю все понятно, хочу сказать лишь то, что q нужно воспринимать как бегунок, он бегает по всем ячейкам от вершины, куда мы его установили вначале: *q = top;, до последнего элемента.

Главная функция

Хорошо, основные функции по работе со стеком мы записали, вызываем.
Посмотрим код:

Вернемся к тому, почему же в функцию мы передавали указатель на указатель вершины. Дело в том, что если бы мы ввели в функцию только указатель на вершину, то «Стек» создавался и изменялся только внутри функции, в главной функции вершина бы как была, так и оставалась NULL. Передавая указатель на указатель мы изменяем вершину *top в главной функции. Получается если функция изменяет стек, нужно передавать в нее вершину указателем на указатель, так у нас было в функции s_push,s_delete_key. В функции s_print «Стек» не должен изменяться, поэтому мы передаем просто указатель на вершину.
Вместо цифр 1,2,3,4,5 можно так-же использовать переменные типа int.

Заключение

Полный код программы:

Так как в стек элементы постоянно добавляются на вершину, выводиться элементы будут в обратном порядке

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

Источник

что такое «выравнивание стека»?

Что такое выравнивание стека? Почему он используется? Может ли он управляться настройками компилятора?

4 ответов

выравнивание переменных в памяти (Краткая история).

в прошлом компьютеры имели 8-битный databus. Это означает, что каждый такт может обрабатывать 8 бит информации. Что было прекрасно.

затем появились 16-битные компьютеры. Из-за нисходящей совместимости и других проблем 8-битный байт был сохранен, а 16-битное слово было введено. Каждое слово было 2 байта. И каждый такт 16 бит информации может быть обработан. Но это поставило небольшой проблема.

давайте посмотрим на карту памяти:

на каждом адресе есть байт, к которому можно получить доступ индивидуально. Но слова можно найти только по четным адресам. Поэтому, если мы читаем слово в 0000, мы читаем байты в 0000 и 0001. Но если мы хотим прочитать слово в позиции 0001, нам нужны две операции чтения. Первый 0000,0001 и тогда 0002,0003 и мы только держать 0001,0002.

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

например, если у нас есть структура с байтовым полем (B) и полем слова (W) (и очень наивным компилятором), мы получаем следующее:

это не весело. Но при использовании выравнивания слов мы находим:

здесь память жертвуется для скорости доступа.

вы можете себе представить, что при использовании double word (4 байта) или quad word (8 байт) это еще более важно. Вот почему с большинством современных компиляторов вы можете выбрать, какой расклад вы используете при компиляции программы.

некоторые архитектуры ЦП требуют определенного выравнивания различных типов данных и будут создавать исключения, если вы не соблюдаете это правило. В стандартном режиме x86 не требует этого для основных типов данных, но может понести штрафы за производительность (проверка www.agner.org для низкоуровневых советов по оптимизации).

на SSE набор инструкций (часто используемый для высокопроизводительного) аудио / видео procesing имеет строгие требования к выравниванию, и бросит исключения, если вы попытаетесь чтобы использовать его на несогласованных данных (если вы не используете, на некоторых процессорах, гораздо медленнее несогласованных версий).

Ваш вопрос наверное что один компилятор ожидает абонента сохранить стек выровнен, в то время как другой ожидает callee для выравнивания стека при необходимости.

редактировать: Что касается того, почему происходит исключение, подпрограмма в DLL, вероятно, хочет использовать инструкции SSE для некоторых временных данных стека и терпит неудачу потому что два разных компилятора не согласны с соглашениями о вызове.

IIRC, выравнивание стека-это когда переменные помещаются в стек, «выровненный» по определенному количеству байтов. Поэтому, если вы используете 16-битное выравнивание стека, каждая переменная в стеке будет начинаться с байта, кратного 2 байтам от текущего указателя стека в функции.

Это означает, что если вы используете переменную

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

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

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

поочередно, найдите переключатель компилятора, который выравнивает переменные в стеке. Очевидно, что синтаксис выравнивания «_ _ declspec», который я использовал здесь, может быть не тем, что использует ваш компилятор.

Источник

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *