Есть ли альтернатива стека + куча + статическая модель памяти?

9

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

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

IKH
источник
Зависит от того, что вы подразумеваете под «стеком». Вы можете поместить кадры стека вызовов в кучу (связать их с помощью указателей). Тогда у вас нет выделенной линейной области памяти для стека, но концептуально у вас все еще есть стек вызовов.
и зависит от того, что вы подразумеваете под «недавно». Я думаю, что локальное хранилище потоков так же стара, как потоки. Но раньше он был доступен через системные вызовы, а теперь новые языки предоставляют вам прямой доступ к нему.
ДХМ
Стек вызовов необходим, потому что процедурные функции должны знать, кто их вызвал, чтобы они могли возвращать результаты и продолжать выполнение. Текущий механизм, если сделать это, на самом деле довольно дешев с точки зрения циклов ЦП и, по крайней мере, с x64, почти все аргументы функций передаются через регистры
James
2
Вы можете найти пост Эрика Липперта " Зачем нужен стек?" представляет интерес. Его главная идея заключается в том, что стек обеспечивает эффективный и простой способ отслеживания областей памяти. Он обсуждает одну альтернативу в нескольких намного более старых должностях, Продолжении, Проходящем Стиль .
Брайан

Ответы:

8

Возможно, вы захотите сделать шаг назад и посмотреть, откуда и почему появились эти существующие модели. Когда процесс создается, ему просто дается плоская область хранения, которая просто индексируется от 0 до N. Поскольку эта область хранения (здесь речь идет об ОЗУ) поддерживается специальным оборудованием и некоторыми модными полупроводниками, это происходит довольно быстро, но это не единственный в своем роде. Другие устройства, такие как жесткие диски, по сути одно и то же, плоское пространство, адресуемое индексом, но на много порядков медленнее.

Причина, по которой существует «куча», заключается в том, что для каждого приложения было бы нецелесообразно пытаться самостоятельно управлять использованием ОЗУ. Еще в тот день, именно так и происходило, программисты заранее планировали, для чего будет использоваться каждая ячейка памяти. По мере того, как программное обеспечение становилось все более сложным, кто-то говорил, не было бы неплохо, если бы я просто пошел к черному ящику и сказал: «Мне нужно 10 байтов, так что дай мне», и мне не нужно было бы беспокоиться обо всех сложных деталях, где и как эти 10 байтов приходят или как они исправлены. Вот что такое куча, на самом деле не получается более простой, чем эта.

Каждый раз, когда создается поток, существуют некоторые структуры данных (и стек), которые получаются с использованием той же «операции gimme», которую я только что описал. Стек используется практически повсеместно, потому что он идеально подходит для фреймов стека вызовов функций и их природы LIFO. Теоретически, каждый вызов функции и локальные переменные могут быть размещены в куче, но это будет слишком дорого по сравнению с несколькими инструкциями по сборке, необходимыми для обновления регистра указателя стека (ESP на x86).

Локальное хранилище потоков (TLS) также строится поверх кучи. Когда поток создается, как часть поездки в кучу для выделения памяти для структур управления, отдельное пространство для TLS также выделяется из кучи.

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

Возьми стек. Это невероятно быстро по сравнению с кучей, но есть два компромисса: 1) вы не контролируете, когда освобождается память; вместо этого, после выхода из функции, что бы вы ни выделяли, и 2) поскольку стеки обычно имеют ограниченный размер, вы должны быть осторожны при размещении больших объемов данных непосредственно в стеке.

Другим типом «модели памяти» является Virtual Memory Manager (VMM), предлагаемый практически каждой крупной ОС через системные вызовы. VMM очень похож на кучу в том смысле, что вы можете запросить любой объем памяти и хранить его столько, сколько хотите. Тем не менее, ограничение заключается в том, что вы можете выделять память только в кратных размерах страниц (например, 4 КБ), поэтому непосредственное использование VMM приведет к большим издержкам в типичном приложении, которое часто выделяет 8-24 байта за раз. Фактически, почти каждая реализация кучи построена поверх VMM специально для целей обеспечения очень общего, специализированного, небольшого выделения блоков. Куча отправляется в VMM всякий раз, когда ей требуется больше памяти, а затем выдает приложению множество маленьких кусочков этой памяти.

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

Другой формой распределителей, вместо непосредственного использования кучи, были бы пулы. Пул - это специализированный распределитель, в котором все блоки имеют одинаковый размер. Пулы (как и стек и TLS) создаются поверх кучи или VMM. Пулы полезны в местах, где вы выделяете много (миллионы) недолговечных небольших объектов одинакового размера. Представьте себе сетевой сервис, обрабатывающий входящие запросы. Каждый клиентский запрос может привести к тому, что одна и та же N-байтовая структура будет выделена для обработки этого запроса. Обмен с использованием пулов заключается в том, что каждый пул обрабатывает только один размер блока (но вы можете создать несколько пулов). Преимущество пулов состоит в том, что, поскольку все объекты имеют одинаковый размер, это не требует сложной логики. Вместо этого всякий раз, когда вам нужен новый блок, он просто дает вам тот, который был недавно освобожден.

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

Помимо того, что я упомянул, я уверен, что есть и другие, более специализированные модели, но, в конце концов, поскольку все основано на плоском адресном пространстве (до тех пор, пока некоторые genuis не придумают какое-то странное-$$ неплоское пространство) ), все это восходит к тому универсальному распределителю «gimme», который является либо VMM, либо кучей.

DXM
источник
1

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

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

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

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

Чтобы подойти к этому с другой стороны, что вы могли бы добавить, что является новым? Вы могли бы поспорить, что такие вещи, как графические / физические процессоры / кэши ЦП / и т. Д., Являются новой областью памяти, но на самом деле они являются отдельным экземпляром или способом ускорения доступа к существующим моделям.

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

Тим Б
источник
4
Большинство людей склонны считать, что нынешний способ является наилучшим / единственным способом, и, если ему дать чистый лист, просто скопируйте то, что уже существует. В других людей являются те , кто на самом деле продвигают технический прогресс. Не сказать, что я лично знаю о каких-либо серьезных конкурирующих моделях (если не считать квантовые компьютеры), но утверждаю, что все, что можно придумать, будет выглядеть так же, как то, что уже существует, по сути, является формой круговых рассуждений.
Aaronaught
@Aaronaught: обратная сторона вашего аргумента заключается в том, что другие люди тратят кучу времени, денег и энергии на нестандартное мышление, и на каждую 1000 (а может быть, гораздо больше) из них можно в конечном итоге добиться технического прогресса, в то время как остальные никуда не денутся , Принимая во внимание, что первая группа, которую можно считать более практичной, принимает эти существующие модели как есть и внедряет инновации поверх них :)
ДХМ
@aaronaught Я думаю, что покрыл это словами "так, пока кто-то не придет к гигантскому концептуальному скачку в некотором роде";) Если у вас есть лучшая альтернативная модель, не стесняйтесь предлагать это ... если нет, то на что-то жаловаться на лицемерия «некоторые люди», когда вы один из них :)
Тим Б
1
@DXM: так? Разве я сказал, что мы все должны вкладывать свое время в исследование новых моделей памяти? Я только указал на (существенный) недостаток в утверждении, что человек может изобретать только те вещи, которые уже были изобретены.
Aaronaught
Претензия, которую я никогда не делал ...
Тим Б.