Как оптимизировать или уменьшить размер ОЗУ в встроенном системном программном обеспечении?

Я работаю над встроенными программными проектами в автомобильной области. В одном из моих проектов прикладное программное обеспечение потребляет почти 99% оперативной памяти. Фактический размер оперативной памяти - 12 КБ. мы используем микроконтроллер Titan F05 TMS470R1B1. Я сделал некоторую оптимизацию, как поиск неиспользуемых сообщений в программном обеспечении и их удаление, но по-прежнему не стоит уменьшать объем оперативной памяти. не могли бы вы предложить несколько хороших способов уменьшить ОЗУ с помощью некоторой оптимизации программного обеспечения?

Ответ 1

В отличие от оптимизации скорости, оптимизация ОЗУ может быть чем-то, что требует "немного здесь, немного там" через весь код. С другой стороны, могут оказаться некоторые "низкие висячие плоды".

Массивы и таблицы поиска

Массивы и справочные таблицы могут быть хорошими "низко висящими фруктами". Если вы можете получить карту памяти из компоновщика, убедитесь, что для больших элементов в ОЗУ.

Проверьте таблицы поиска, которые неправильно использовали объявление const, которое помещает их в ОЗУ вместо ПЗУ. Особенно обратите внимание на поисковые таблицы указателей, которым требуется const на правильной стороне *, или могут потребоваться две объявления const. Например:.

const my_struct_t * param_lookup[] = {...};  // Table is in RAM!
my_struct_t * const param_lookup[] = {...};  // In ROM
const char * const strings[] = {...};    // Two const may be needed; also in ROM

Стек и куча

Возможно, ваш конфигуратор компоновщика резервирует большое количество ОЗУ для кучи и стека, больше, чем необходимо для вашего приложения.

Если вы не используете кучу, вы можете это исключить.

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

Другие

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

Глобальные и локальные переменные

Проверить ненужное использование static или глобальных переменных, где вместо этого может использоваться локальная переменная (в стеке). Я видел код, который нуждался в небольшом временном массиве в функции, объявленной static, очевидно, потому что "для этого потребовалось бы слишком много пространства стека". Если это так будет происходить в коде, это фактически сэкономит общее использование памяти, чтобы снова сделать такие переменные локальными. Это может потребовать увеличения размера стека, но сэкономит больше памяти на уменьшенных глобальных/статических переменных. (В качестве побочного преимущества функции с большей вероятностью будут повторными, потокобезопасными.)

Меньшие переменные

Переменные, которые могут быть меньше, например. int16_t (short) или int8_t (char) вместо int32_t (int).

Размер переменной Enum

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

Выполнение алгоритма

Исправьте свои алгоритмы. Некоторые алгоритмы имеют ряд возможных реализаций с компромиссом скорости/памяти. Например. Шифрование AES может использовать вычисление ключа "на лету", что означает, что вам не нужно иметь весь расширенный ключ в памяти. Это экономит память, но медленнее.

Ответ 2

Удаление неиспользуемых строковых литералов не повлияет на использование ОЗУ, поскольку они не хранятся в ОЗУ, а в ПЗУ. То же самое касается кода.

Что вам нужно сделать, это сократить фактические переменные и, возможно, размер вашего стека/стеков. Я бы поискал массивы, которые могут быть изменены и неиспользуемы. Кроме того, лучше избегать динамического распределения из-за опасности фрагментации памяти.

Кроме того, вы захотите убедиться, что постоянные данные, такие как таблицы поиска, хранятся в ПЗУ. Обычно это достигается с помощью ключевого слова const.

Ответ 3

Убедитесь, что компоновщик создает файл MAP - он покажет вам, где используется ОЗУ. Иногда вы можете найти такие вещи, как строковые литералы/константы, которые хранятся в ОЗУ. Иногда вы обнаружите, что есть неиспользуемые массивы/переменные, помещенные там кем-то другим.

Если у вас есть файл карты компоновщика, он также легко атакует модули, которые используют первую RAM.

Ответ 4

Вот трюки, которые я использовал в соте:

  • Начните с очевидного: сжимайте 32-битные слова в 16s, где это возможно, перегруппируйте структуры, чтобы исключить прокладку, вырезать слабину в любых массивах. Если у вас есть какие-либо массивы из более чем восьми структур, стоит использовать бит-поля, чтобы скомпоновать их.
  • Удалите динамическое распределение памяти и используйте статические пулы. Постоянный объем памяти намного легче оптимизировать, и вы будете уверены в отсутствии утечек.
  • Ограничьте локальные распределения, чтобы они не оставались в стеке дольше, чем нужно. Некоторые компиляторы очень плохо понимают, когда вы закончите с переменной, и оставите ее в стеке, пока функция не вернется. Это может быть плохо с большими объектами во внешних функциях, которые затем потребляют постоянную память, которой они не имеют, поскольку внешняя функция глубже проникает в дерево.
  • alloca() не очищается до тех пор, пока функция не вернется, поэтому можно потратить стек дольше, чем вы ожидаете.
  • Включить тело функции и постоянное слияние в компиляторе, так что, если он увидит восемь разных констант с одинаковым значением, он поместит только один в текстовый сегмент и будет псевдоним с компоновщиком.
  • Оптимизировать исполняемый код для размера. Если у вас жесткий крайний срок, вы точно знаете, как быстро ваш код нужно запускать, поэтому, если у вас есть запасная производительность, вы можете сделать компромисс скорости и размера, пока не достигнете этого момента. Рулоны, вытаскивают общий код в функции и т.д. В некоторых случаях вы можете получить улучшение пространства путем вложения некоторых функций, если служебные данные пролога/эпилога больше, чем тело функции.

Последнее относится только к архитектурам, которые хранят код в ОЗУ. Я думаю.

Ответ 5

w.r.t, следующие - ручки для оптимизации ОЗУ

  • Убедитесь, что количество параметров, переданных функциям, глубоко проанализировано. На ARM-архитектурах согласно AAPCS (ARM arch Procedure Call standard) максимум 4 параметра могут быть переданы с использованием регистров, и остальные параметры будут помещены в стек.

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

  • Чем глубже вызовы функций, тем тяжелее использование стека. используйте любой инструмент статического анализа, чтобы узнать, как вызвать вызов вызова, и искать места, чтобы уменьшить его. Когда функция A вызывает функцию B, B вызывает C, которая, в свою очередь, вызывает D, которая, в свою очередь, вызывает E и идет глубже. В этом случае регистры не могут быть на всех уровнях для передачи параметров, и поэтому очевидно, что будет использоваться стек.

  • Попытайтесь найти места для объединения двух параметров в один, где это применимо. помните, что все регистры имеют 32 бит в ARM, и поэтому возможна дальнейшая оптимизация. void abc (bool a, bool b, uint16_t c, uint32_t d, uint8_t e)//использует регистры и стек void abc (uint8 ab, uint16_t c, uint32_t d, uint8_t e)//первые 2 параметра могут быть забиты. поэтому общее количество 4 параметров может быть передано с использованием регистров

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

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

рассматривает

barani kumar venkatesan

Ответ 6

Добавление к предыдущим ответам.

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

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