Войти
ПрограммированиеФорумГрафика

Modern C++ stack allocation (3 стр)

Страницы: 1 2 3 4 Следующая »
#30
15:12, 2 мая 2016

Barabus
> выделенная область памяти
Так это и есть стек.

#31
15:26, 2 мая 2016

да, про стек Barabus загнул. Это основополагающая вещь

#32
15:34, 2 мая 2016

Delfigamer
> Так это и есть стек.
Не, стек — это не просто область памяти, это и метод адресации, и специальные инструкции типа push/pop. Область памяти под параметры может адресоваться и индексами и это будет уже не стек.

К примеру, под DOS был контейнер COM, в который собирались программы, содержащие только сегмент кода. И никаких call, тупо jmp. Ни ни сегмента стека, ни сегмента данных там предусмотрено не было.

Но нет никаких препятствий реализовать функции и при такой модели, к примеру выделить один регистр под указатель на массив параметров и вписывать туда адрес перед вызовом jmp. Не так удобно, как push/pop, но тоже работает.

#33
16:10, 2 мая 2016

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

#34
16:14, 2 мая 2016

-Eugene-
> Если я правильно помню, стек там прекрасно жил на конце сегмента кода.
Не буду спорить, ибо изучал все это более 15 лет назад.

Может там и есть стек и я что-то путаю.

upd: Прерывания из COM дергать было можно, все же стек там должен быть, да.

#35
23:24, 2 мая 2016

Barabus
> Не, стек — это не просто область памяти, это и метод адресации, и специальные
> инструкции типа push/pop.
Нет, стек - это LIFO, а на реализацию по барабану.

UPD да что ж такое
#36
23:39, 2 мая 2016

Barabus
> Дык есть alloca/malloca
Suslik
> Modern C++


-Eugene-
> Ээ... На уровне синтаксиса?
> Но ведь alloca - это и есть обычная функция, только compiler-specific.
в том смысле, что на уровне языка невозможно написать свой стековый аллокатор без хаков, потому что возвращаемая из функции аллокации память должна быть освобождена по окончании работы этой функции by design. поэтому нужны либо ассемблерные хаки, либо, как я сказал раньше, реализация на уровне синтаксиса языка.

#37
9:11, 3 мая 2016

Barabus
> upd: Прерывания из COM дергать было можно, все же стек там должен быть, да.

.com это вообще наследие CP/M. 8-битное легаси с 64k ОЗУ. Файл программы как есть (т.е. никаких заголовков и секций в .com файле нет) грузится по адресу 0x200, если не ошибаюсь, и оттуда же и стартует. А в первых 0x200 байтах располагаются служебные структуры, в том числе таблица прерываний и то ли 2 то ли 4 File Control Block, для операций с диском. Будучи уже сегментным x86 DOS на всякий случай позаимствовал эту 8-битную спецификацию, просто все сегментные регистры выставлялись на один и тот же адрес. Модель памяти tiny.

При этом x86 имел уже продвинутую стековую архитектуру, когда разрабы процессоров и компиляторов чётко поняли что им нужно от стека и в каком виде. Система команд и регистров позволяла, например, индексировать массив в стеке одной командой. Специальный регистр BP (base pointer) дублировал индексацию стека как раз для удобства отделения параметров от локальных переменных. Даже типовые операции с регистрами SP и BP при входе в процедуру были задублированы инструкциями ENTER/LEAVE, чтобы сэкономить на байтах, чем, с удовольствием пользовался, например, turbo pascal. В общем, на момент создания, x86 полностью раскрыл в своей архитектуре современную концепцию стека.

8-битные процессоры же нередко создавались еще без стекового царя в голове и были в этом смысле неудобными.
Например MOS 6052 в качестве регистра стека имел всего 1 байт. Стек всегда располагался во второй странице (256 байт) памяти и собственно не мог быть больше 256 байт. Фактически предполагалось что он будет хранить только адреса возвратов из прерываний и процедур. В целом он был непригоден для хранения параметров и локальных переменных, хотя PUSH и POP были. Интересно, что это перекликается с архитектурой первых версий BASIC, когда переход на подпрограмму осуществлялся как GOSUB номер_строки, а параметры надо было пропихивать в глобальных (а других и не было) переменных. Собственно концепция реентерабельности еще только назревала. Сама по себе она была жутко неоптимальна на этом процессоре.
В Intel 8080 пошли немного дальше - SP был уже 16-битным, и PUSH/POP можно было делать для регистровых пар. Но чтобы адресовать параметры или переменные относительно стека требовалось три жирных инструкции в общем случае с порчей регистра HL. Привычный ныне стек был всё еще дорогой штукой. Забавно, что особое отношение к стеку было зафиксировано даже в железе - одна из ножек процессора сигнализировала о том идёт считывание/запись в память от любой другой инструкции или инструкции использующей для адресации SP - что позволяло реализовать для стека отдельную от основной память.
Из известных мне 8-битных процов лёд начала ломать Zilog с её бессмертным Z80, он расширял архитектуру 8080 (был совместим), добавляя в неё 2 индексных регистра IX и IY и новый режим адресации с ними - косвенный с 8-битным смещением. Т.е. ячейка памяти с адресом как сумма IX или IY с непосредственно указываемого в инструкции знакового байта. Т.е., если при входе в процедуру скопировать SP в IX, то можно адресовать одной инструкцией параметры или локальные переменные. Инструкция длинная, но всё же одна. Однако, если требовался больший охват памяти, чем +-127 байт, то опять появлялась проблема, т.к. смещение могло быть только байтовое. Однако для 8-битного процессора это было маловероятно. Кроме того, индексация массива в стеке тоже требовала определенных телодвижений.

В общем борьба за стек с человеческим лицом имела длинную историю.

#38
10:27, 3 мая 2016

=A=L=X=
> Из известных мне 8-битных процов лёд начала ломать Zilog с её бессмертным Z80,

А ещё копирование блока памяти через стек было самым быстрым. Помню, аж кипитком писал, когда сам дошёл до такого решения. Правда, всё было внутри пар DI/EI. Я даже I использовал  :)

Вспоминаешь себя в 15 лет, аж слёзки наворачиваются

#39
10:46, 3 мая 2016

=A=L=X=
О, прикольно, спасибо за лекцию.
Зато потом дизайнерская мысль развернулась во всю. В m68k было 8 32битных адресных регистров, любой из которых мог работать как указатель стека (один из них был все равно главнее, но по мелочи).  push/pop были просто режимом адресации и были применимы к любой инструкции, работающей с памятью (можно было одним mov'ом запопать один стек и сразу запушить в другой), затрахаешься весь этот треш эмулировать.

#40
11:44, 3 мая 2016

Suslik
А для чего тебе нужен массив переменной длины именно на стеке?

#41
11:46, 3 мая 2016

dds
см прошлую страницу

#42
11:54, 3 мая 2016

Suslik
> за хлебом. существуют случаи, когда память хотелось бы уметь выделять и
> освобождать динамически быстро. например, для многих алгоритмов на матрицах
> нужно использовать промежуточные матрицы, которые надо как-то выделять и где-то
> хранить. если матрицы имеют средний размер, то выделять их на куче — слишком
> затратно, а хранить на стеке по максимальному размеру — нерационально. уже
> говорил, что ODE использует для этого костыли вроде alloca, задефайненные для
> каждого компилятора, в то время как аллокация на стеке — это операция, которая
> не может быть реализована через обычную функцию by design, её можно
> по-нормальному реализовать только на уровне синтаксиса языка.

А на сколько проседает производительность, если вызвать 1 malloc/new[] для размещения этих матриц? У тебя ботлнек на аллокации?

#43
11:55, 3 мая 2016

Hardcode
> О, прикольно, спасибо за лекцию.

Прикольно на всяких RISC, типа PowerPC

#44
11:59, 3 мая 2016

Hardcode
Однако, практический опыт показывает, что в нынешних условиях RISKи предпочтительнее.
Их, кстати, я и имел в виду в #35. С железной точки зрения, никаких стеков нет, все регистры равноправны (кроме r0). Но это не мешает компиляторам делать рекурсии, исключения, варарги и прочие ништяки.

innuendo
Хотя, в конечном счёте, роль SP назначили на r1 и о проблеме благополучно забыли.
Зато можно прикольно реализовать str[++i].

Страницы: 1 2 3 4 Следующая »
ПрограммированиеФорумГрафика

Тема в архиве.