Есть ли недостаток в выделении огромного количества стека для одного массива во встроенной системе?

12

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

Я работаю над существующим кодом, который пытаюсь улучшить (дизайн, возможные проблемы, производительность и т. Д.). Этот код работает на старом 8-битном MCU с 4 КБ ОЗУ . В этом коде я сталкиваюсь с использованием массива почти 1 КБ (да, 1 КБ в системе ОЗУ 4 КБ ). Каждый байт этого массива используется, это не вопрос. Проблема в том, что этот массив является статическим массивом в файле, в котором он объявлен, поэтому его жизненный цикл такой же, как и у программного (то есть его можно считать бесконечным).

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

Теперь вопрос: будет ли это хорошей идеей? С точки зрения дизайна, если ему не нужен бесконечный / глобальный жизненный цикл, он принадлежит стеку. Но эй, это 1 КБ из 4 КБ, нет ли недостатка в выделении 25% ОЗУ, как это? (это может быть 50% или более от стека)

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

Единственное, что я осознаю, это то, что я должен убедиться, что у меня на самом деле есть 1 КБ стека при входе в эту функцию. Может быть, это все, что я должен позаботиться, а может и нет.

Тим
источник
4
Вы написали: «и, следовательно, сохранить этот 1 КБ оперативной памяти». Сохранить это для чего? Это 1 КБ должно быть доступно, когда вам это нужно для массива, так почему бы не сделать статическое распределение? У вас есть другое использование памяти, когда она не нужна для массива?
kkrambo
@kkrambo В какой-то момент мы считаем, что система переполнена, когда мы не можем добавить больше ничего в ОЗУ, будь то что-то статичное или в стеке. Если мы поместим этот массив в стек только тогда, когда мы его используем, он предоставит место другим функциям, если они не используются одновременно. Но вопрос совершенно законный, в настоящий момент, если мы ничего не меняем в SW, нам не нужно больше оперативной памяти;)
Тим
1
Не могли бы вы уточнить, имеет ли этот массив содержимое, которое всегда остается неизменным, или же оно изменяется при вызове функции, которая его использует?
Blrfl
@Blrfl Он меняется каждый раз, когда вызывается функция.
Тим

Ответы:

8

Единственное, что я осознаю, это то, что я должен убедиться, что у меня на самом деле есть 1 КБ стека при входе в эту функцию.

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

Кстати, некоторые дешевые микропроцессоры могут использовать «большой» кадр вызова более дорогостоящим, чем «нормальный» (например, потому что их набор команд предпочтет однобайтовое смещение от указателя стека). YMMV.

Кроме того, если вы пишете код на C и чувствуете, что ваш большой массив может повторно использовать пространство для других целей, вы можете рассмотреть возможность сделать его членом объединения (с глобальной переменной unionтипа). Да, это довольно некрасиво.

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

Василий Старынкевич
источник
1
Спасибо, я на самом деле немного ожидал такого ответа, т.е. держал его статически распределенным, чтобы быть уверенным, что у меня не будет переполнения стека. К сожалению, у меня нет недавнего компилятора GCC, и код не маленький.
Тим
Разве вы не можете получить (возможно, путем компиляции GCC из его исходного кода) компилятор GCC для вашей платформы?
Василий Старынкевич
2
У меня уже есть один, он просто очень старый. Работа над новым не входит в мои задачи и не вписывается в мой график. Но конечно, некоторые вещи будут проще с современными инструментами.
Тим
3
ИМХО стоит обратиться к своему начальнику с просьбой предоставить вам более новый GCC (либо предоставив вам новейшие бинарные инструменты, либо предоставив вам время для перекомпиляции GCC), поскольку вполне вероятно, что более новый GCC оптимизировал бы немного лучше (знаете ли вы gcc -flto -Os? ) и вы могли бы получить немного памяти ....
Василий Старынкевич
2
Это на ходу, но это не придет раньше. Я уже использую -Os в других системах с более новыми инструментальными цепочками, но я не знал об этом параметре -flto, спасибо за указание на это. (Обратите внимание, что я провел несколько тестов с GCC -Os и параметром -O1-3 несколько недель назад на GCC 5.4.1 и ОЗУ все время было одинаковым. Хотя время выполнения FLASH и MCU было разным. Это был не тот MCU, который я упоминаю в этом Q / A)
Тим
6

Люди, как правило, осторожны с большим стеком, потому что он увеличивается в ОЗУ и перезаписывает значения переменных, что приводит к необъяснимому поведению. Это становится еще хуже, потому что вам нужно знать минимально возможный адрес указателя стека и вычитать размер, выделяемый при входе в подпрограмму.

Это все работа для аппаратного управления памятью (должна генерировать ловушки или сбои при переполнении стека) или для компилятора, если у него есть функции для этого вида анализа.

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

Мартин Сугиоарто
источник
4

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

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

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

Мако
источник
1

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

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

Особенно при работе со встроенными системами вы хотите, чтобы как можно больше сбоев происходило во время компиляции, и ничего не происходило во время выполнения (хотя, если бы мы могли этого добиться, было бы неплохо ...)

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

tofro
источник