Навіщо нам потрібна купа, якщо все можна зробити набагато ефективніше на Стек?


24

Це насправді пов'язане з питанням, яке я задав вчора про те, чому і стек, і купа необхідні в додатках, які ми використовуємо сьогодні (і чому ми не можемо просто перейти з Heap замість обох, щоб мати простий & єдиний стандарт, який потрібно пройти).

Однак багато відповідей вказували на те, що стек є незамінним через те, що в сотні (або тисячі) разів швидше, ніж намагання виділити / посилатись на Кучу. Я знаю, що існує проблема з динамічним розподілом пам’яті, якщо ми скасуємо Купу, але чи не існує способу цього покращити чи, можливо, спосіб покращити стек, щоб він міг обробляти динамічне розподілення пам’яті?


4
Два уривки з попереднього запитання: "найважливіший недолік - це обмеження місця, а тому зберігання в ньому великих предметів або намагання використовувати його для довгоживучих об'єктів - це і погані ідеї", і "стеки - надзвичайно ефективні структура для управління даними, що підкоряються LIFO (останньому в першому) правилам ".
Каскабель

2
Ваше приміщення несправне - не все можна зробити набагато ефективніше на стеці. Це не суперечить отриманим відповідям - що те, що можна зробити на стеці, там можна зробити набагато швидше.
Інго

... якщо припустимо, що у вашої апаратури є стек або відношення до стека.
Рітч Мелтон

3
Я переконаний. Я кажу, зробіть це.
JeffO

Відповіді:


25

Проблема стеків полягає в тому, що ви не можете "звільнити" пам'ять, якщо вона не знаходиться на вершині стека. Наприклад, скажімо, що ви виділили 3 речі різного розміру:

a = allocate(2000000); // 2000000 bytes
b = allocate(1);
c = allocate(5000000);

Стек був би aзнизу, bпосередині та cзверху. Це стає проблематичним, якщо ми хочемо звільнити b:

free(b); // b is not on top! We have to wait until c is freed!

Вирішення проблеми полягає в переміщенні всіх даних після bта переміщенні, якщо так, щоб вони надходили після a. Це працює, але в цьому випадку знадобиться 5000000 примірників - те, що буде набагато повільніше, ніж купа.

Ось чому ми маємо купу. Хоча розподіл може бути повільнішим, ніж стек ( O(log n)vs O(1)), купи дозволяють звільнити пам'ять у довільному місці швидко - O(log n)порівняно з стекомO(n)


4
З цим пов’язано те, що Facebook не видаляє вміст зі свого диска, коли ви попросите його видалити, він просто видаляє вказівник на нього. Очевидно, що накладні витрати на спробу дефрагментації або спроби знайти еквівалентний пробіл на диску занадто багато часу забирають за швидкістю запису даних, тому вони просто додають все на меті високої води на диску.
Пол Томблін

Ну, диски facebook можна розглядати як купу. І я впевнений, що у них є якийсь збір сміття для дисками.
deadalnix

2
@deadalnix Насправді це приклад використання величезного стека замість купи, яка зазвичай використовується для більшої кількості пам'яті. Facebook, однак, є особливим випадком. Дані додаються набагато швидше, ніж видаляються, що розселення не суттєво впливає на швидкість зростання - ви можете навмисно включати витоки пам’яті в дизайн, щоб отримати це розподіл O (1).
Том Кларксон

7
@PaulTomblin Основна причина, за якою FB не видаляє вміст, полягає в тому, що вони зможуть видобути його з метою отримання прибутку ...
quant_dev

5
Facebook doesn't remove content from its disk when you ask it to remove it, it just removes the pointer to it- По суті, це відбувається, коли ви робите звичайне видалення файлу в будь-якій операційній системі.
Роберт Харві

5

Стек складається за нитку, Купа - це процес

Якщо у вас є 100 ниток, усі робочі елементи, які я обробляю, я поміщаю в чергу, куди я розподіляю такі елементи, щоб хтось із 100 потоків міг їх бачити?

Є й інші типи пам’яті

Напр. Файли, зіставлені з пам'яттю, спільна пам'ять, введення / виведення (карта ядра). Аргумент ефективності є своєрідним суперечкою в цих ситуаціях.


4

Стек - це структура LIFO (останній-в-першому), на верхню частину якої зберігається опорний вказівник (зазвичай підтримується апаратним забезпеченням). Сказавши це, все, що ви намагаєтеся виділити на стек замість купи, повинно бути локальною змінною у кожній функції вгорі цього стека. Отже, головна причина проти стека полягає в тому, що вашій основній () програмі потрібно буде достроково виділити всі структури даних, якими користується ваша програма (які призначені для роботи протягом усієї тривалості вашої програми), як і всі структури даних, виділені в межах функціональних викликів згодом буде видалено, коли ці функціональні дзвінки повернуться, а їхні кадри чи записи активації вискакують із стека.


3
LIFO, а не FIFO.
Паббі

іноді також називають FILO;)
eenone

3

Стеки чудово підходять для розподілу пам’яті, що підкоряються правилам Last in First out (LIFO), тобто ви звільняєте пам'ять у точному зворотному порядку, за яким ви її розподіляєте. LIFO - це дуже поширена, можливо, найпоширеніша схема розподілу пам'яті. Але це не єдиний візерунок або навіть єдиний загальний зразок. Щоб скласти ефективну програму, яка може вирішити найрізноманітніші проблеми, ми повинні враховувати менш поширені моделі, навіть якщо це означає більш складну інфраструктуру.

Якщо я можу отримати мета для абзацу: ви початківець, ви як початківець цінуєте простоту та чорно-білі правила. Однак ви, як початківець, маєте лише глибокий погляд на коло проблем та обмежень, які мають бути задоволені комп’ютерними програмами. Ви впроваджуєте технологію, яка вже 75 років активно розвивається. Немає нічого поганого в тому, щоб запитати, чому все так, як вони є, але, як правило, відповідь буде "Так, ми спробували простий, прямолінійний метод 50 років тому, і виявилося, що це не дуже добре працює для цілих класів проблем, тож нам довелося зробити щось складніше ". По мірі прогресу технологій простота, як правило, повинна поступатися місцем ефективності та гнучкості.


0

Для іншого прикладу, закриття. Якщо ви стек поп, то ви можете втратити запис активації завершення. Тож якщо ви хочете, щоб ця анонімна функція та її дані зберігалися, вам доведеться зберігати її де-небудь, крім стеку виконання.


0

Серед багатьох інших причин виділення накопичувальних купи.

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

Ви можете передати адреси елементів стеку з вашої функції до функцій, які ви викликаєте, оскільки ви можете гарантувати, що вони існуватимуть, поки ваша програма не повернеться (). Але, як тільки ваша функція повернеться, все сховище стека буде захоплено.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.