Чи є недолік при виділенні величезної кількості стека для одного масиву у вбудованій системі?


12

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

Я працюю над існуючим кодом, який намагаюся вдосконалити (дизайн, можливі проблеми, вистави тощо). Цей код працює на старому 8-бітовому MCU з лише 4 КБ оперативної пам’яті . У цьому коді я зіткнувся з використанням масиву майже 1 КБ (так, 1 КБ в системі оперативної пам’яті 4 КБ ). Кожен байт цього масиву використовується, це не питання. Проблема полягає в тому, що цей масив є статичним масивом у файлі, де він оголошений, тому його життєвий цикл такий же, як і програмний (тобто його можна вважати нескінченним).

Однак, прочитавши код, я просто з’ясував, що цей масив не потребує нескінченного життєвого циклу, він побудований і розглядається цілком процедурно, тому ми повинні мати можливість оголосити його лише у функції, де він використовується, таким чином, це було б на стеці, і тому ми зберегли б цей 1 КБ оперативної пам’яті.

Тепер питання: чи це було б гарною ідеєю? З точки зору дизайну, якщо йому не потрібен нескінченний / глобальний життєвий цикл, він належить до стека. Але ей, це 1 КБ з 4 КБ, хіба немає недоліків виділення 25% оперативної пам’яті, як це? (це може становити 50% або більше стека)

Може хтось поділиться певним досвідом подібної ситуації, чи хтось задумається про будь-яку поважну причину не поставити цей масив у стек? Я шукаю технічні недоліки, а також коментарі щодо дизайну.

Єдине, що я усвідомлюю, це те, що я маю переконатися, що я фактично маю 1 КБ стека безкоштовно при введенні цієї функції. Можливо, це все, що я маю подбати, а може й ні.


4
Ви написали, "і тому збережіть цей 1 КБ оперативної пам'яті". Зберегти це для чого? Цей 1 КБ повинен бути доступний, коли він вам потрібен для масиву, то чому б не зробити статичний розподіл? Чи є у вас інше використання для пам'яті, коли вона не потрібна для масиву?
kkrambo

@kkrambo В якийсь момент ми вважаємо, що система повна, коли ми не можемо нічого більше додати до оперативної пам’яті, будь то статичне або на стеці. Якщо ми помістимо цей масив у стек лише тоді, коли ми його використовуємо, він дозволить розмістити ще одну функціональність, якщо вони не використовуються одночасно. Але питання є цілком законним, на даний момент, якщо ми нічого не змінимо в SW, нам більше не потрібно оперативної пам’яті;)
Tim

1
Чи можете ви уточнити, чи має цей масив вміст, який завжди залишається тим самим, чи він змінюється, коли викликається функція, яка його використовує?
Blrfl

@Blrfl Він змінюється щоразу, коли функція викликається.
Тім

Відповіді:


8

Єдине, що я усвідомлюю, це те, що я маю переконатися, що я фактично маю 1 КБ стека безкоштовно при введенні цієї функції.

Так, і це сильне обмеження. Ви краще будете статично впевнені, ніж у вас є такий великий простір у стеці. Якщо код невеликий, якщо для складання коду ви використовуєте недавній GCC , перегляньте це .

До речі, деякі дешеві мікропроцесори можуть використовувати "великий" кадр виклику дорожче, ніж "звичайний" (наприклад, тому, що їх набір інструкцій сприятиме однобайтовому зміщенню від покажчика стека). YMMV.

Крім того, якщо ви кодуєте в C і якщо вам здається, що ваш великий масив може повторно використовувати його простір для інших цілей, ви можете розглянути можливість створення його членом об'єднання (з глобальною змінною unionтипу). Так, це дуже некрасиво.

Крім того, ви можете розглянути можливість кодування якогось примітивного розподільника купи, що підходить до вашої програми (і він може мати API, відмінний від malloc& free....).


1
Дякую, я насправді трохи очікував такої відповіді, тобто тримати її статично розподіленою, щоб бути впевненим, що я не закінчуюсь переповненням стека. На жаль, у мене не останній компілятор GCC, і код не малий.
Тім

Ви не можете отримати (можливо, склавши GCC зі свого вихідного коду) компілятор GCC для своєї платформи?
Базиль Старинкевич

2
У мене його вже є, він просто справді старий. Робота над новою не входить в міру, і це не вписується в мій графік. Але звичайно, деякі речі були б простішими з сучасними інструментами.
Тім

3
Попросити свого начальника отримати вам новіший GCC (або надавши вам сучасні бінарні інструменти, або давши вам час на перекомпіляцію GCC) варто IMHO, тому що, напевно, новіший GCC оптимізується трохи краще (ви знаєте про це gcc -flto -Os? ) і, можливо, ви отримаєте трохи пам’яті ....
Базиль Старинкевич

2
Це на шляху, але це не дійде до певного часу. Я вже використовую -O в інших системах з більш новими ланцюжками інструментів, але мені не було відомо про цей параметр -flto, дякую, що вказав на це. (Зауважте, що я кілька тижнів тому на GCC 5.4.1 я робив кілька тестів з параметром GCC -O та -O1-3, і оперативна пам’ять постійно була однаковою. Хоча час роботи FLASH та MCU були різними. це не було на той же MCU , як один я згадую в цьому Q / A)
Тім

6

Люди схильні бути обережними з великим стеком, тому що він зростає в оперативній пам'яті і перезаписує значення змінних, що призводить до незрозумілої поведінки. Це стає ще гірше, тому що вам потрібно знати найменшу можливу адресу вказівника стека та відняти розмір, який слід виділити при введенні в розпорядок.

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

В іншому випадку ви можете робити все, що завгодно, зі своєю ОЗУ.


4

Як вказували попередні відповіді, я також спочатку рекомендую залишити масив статичним, якщо він підходить до пам'яті. У більшості випадків набагато важливіше мати детермінований слід пам’яті, навіть якщо це означає, що ви «витрачаєте» пам’ять на змінні, які не використовувалися весь час. Якщо розмістити великі масиви у свій стек, це надто легко вибухне, а переповнення стека, як правило, спричинить важко знайти та важко відтворити проблеми (якщо ви не можете використовувати MMU для захисту стека).

Пропозиція поділитися блоком з деякими іншими даними за допомогою об'єднання є дійсним IMO, хоча воно також може бути джерелом для важко знайти проблем, якщо ви знайдете в ньому неправильні змінні.

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


1

У вас є ще один варіант, якщо у вас є якесь флеш-пам’ять. Ви можете торгувати швидкістю доступу для оперативної пам’яті, зберігаючи свої дані у спалах, читаючи та шукаючи там. Вам потрібно буде завантажувати один раз за один раз у таран. Це буде дещо складніше, якщо вам потрібно буде оновити записи. Вам знадобиться сегментований механізм вирівнювання зносу. Я робив це раніше і включав індекс для прискорення доступу.


1
Я також робив це в минулому, але моє запитання було більше про проблеми, з якими я можу зіткнутися, якщо я ставлю це на стек, а не насправді про альтернативи ОЗУ, коли нам цього не вистачає. Дякуємо, що все одно відповіли
Тім

1

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

Створення великих масивів, які можуть знадобитися у довільних станах вашої програми, статично розподілених робить саме це - Linker врешті-решт попередить вас, "це не вписується в оперативну пам'ять", тоді як розподіл стеків просто призведе до краху вашої програми з важким для налагодження стеком переповнення.

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