Я использую слишком много ОЗУ. Как это можно измерить?
Я хотел бы знать, сколько оперативной памяти, которую я использую в своем проекте, насколько я могу судить, нет никакого способа на самом деле справиться с этим (за исключением того, что я сам и сам его вычисляю). У меня есть сцена в довольно большом проекте, где я определил, что у меня заканчивается RAM.
Я определил это, потому что я могу добавить раздел, а затем весь ад сломается где-то еще в моем коде без видимых причин. Если я #ifndef
что-то еще, он снова работает. В новом коде нет ничего неправильного программирования.
Я некоторое время подозревал, что добираюсь до конца доступной ОЗУ. Я не думаю, что я использую слишком много стека (хотя это возможно), что лучший способ определить, сколько оперативной памяти я фактически использую?
Пройдясь и пытаясь разобраться в этом, у меня возникают проблемы, когда я перехожу к перечислениям и структурам; сколько памяти они стоят?
first edit: ТАКЖЕ, я редактировал мой эскиз так много, начиная с момента запуска, это не фактические результаты, которые я изначально получил, но они то, что я получаю сейчас.
text data bss dec hex filename
17554 844 449 18847 499f HA15_20140317w.cpp.elf
16316 694 409 17419 440b HA15_20140317w.cpp.elf
17346 790 426 18562 4882 HA15_20140317w.cpp.elf
Первая строка (с текстом 17554) не работала, после большого редактирования вторая строка (с текстом 16316) работает так, как должна.
edit: в третьей строке есть все работающее, серийное чтение, мои новые функции и т. д. Я по существу удалил некоторые глобальные переменные и дублирующий код. Я упоминаю об этом, потому что (как подозревали), речь идет не об этом коде на саи, а о ОЗУ. Это возвращает меня к первоначальному вопросу: «Как лучше всего измерить его» Я все еще проверяю ответы, спасибо.
Как я действительно интерпретирую вышеуказанную информацию?
До сих пор я понимаю:
`TEXT` is program instruction memory
`DATA` is variables (unitialised?) in program memory
`BSS` is variables occupying RAM
, поскольку BSS значительно меньше 1024 байта, почему вторая работа, но первая не работает? Если это DATA+BSS
, то они занимают более 1024.
перередактировать: Я редактировал вопрос, чтобы включить код, но теперь я удалил его, потому что он действительно не имел ничего общего с проблемой (кроме, может быть, плохого кодирования , объявления переменных и т.п.). Вы можете просмотреть код, просмотрев его, если вы действительно хотите его увидеть. Я хотел вернуться к обсуждаемому вопросу, который был более основан на: Как измерить использование ОЗУ.
5 ответов
Вы можете использовать предоставленные функции AVRGCC: использование стека мониторинга
Функция предназначена для проверки использования стека, но то, что она сообщает, является фактическим ОЗУ, которое никогда не использовалось (во время выполнения). Это делается путем «рисования» (заполнения) ОЗУ с известным значением (0xC5), а затем проверки области ОЗУ, подсчитывающей, сколько байтов имеет все то же начальное значение.
В отчете будет отображаться ОЗУ, которая не была использована (минимальная свободная ОЗУ), и поэтому вы можете вычислить максимальную ОЗУ, которая была использована (Total RAM - RAM).
Существуют две функции:
-
StackPaint выполняется автоматически во время инициализации и «краска» ОЗУ со значением 0xC5 (при необходимости может быть изменено).
-
StackCount можно вызвать в любой точке, чтобы подсчитать RAM, которая не была использована.
Вот пример использования. Не делает многого, но предназначен, чтобы показать, как использовать функции.
// -----------------------------------------------------------------------------
extern uint8_t _end;
extern uint8_t __stack;
void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
void StackPaint(void)
{
#if 0
uint8_t *p = &_end;
while(p <= &__stack)
{
*p = 0xc5;
p++;
}
#else
__asm volatile (" ldi r30,lo8(_end)\n"
" ldi r31,hi8(_end)\n"
" ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
" ldi r25,hi8(__stack)\n"
" rjmp .cmp\n"
".loop:\n"
" st Z+,r24\n"
".cmp:\n"
" cpi r30,lo8(__stack)\n"
" cpc r31,r25\n"
" brlo .loop\n"
" breq .loop"::);
#endif
}
uint16_t StackCount(void)
{
const uint8_t *p = &_end;
uint16_t c = 0;
while(*p == 0xc5 && p <= &__stack)
{
p++;
c++;
}
return c;
}
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(StackCount(), DEC); // calls StackCount() to report the unused RAM
delay(1000);
}
Основные проблемы, которые могут возникнуть при использовании памяти во время выполнения, следующие:
- Отсутствует доступная память в куче для динамических распределений (
malloc
илиnew
) - при вызове функции не осталось места в стек
Оба на самом деле такие же, как AVR SRAM (2K на Arduino) используется для обоих (помимо статических данных , размер которых никогда не изменяется во время выполнения программы).
Как правило, динамическое распределение памяти редко используется на MCU, только несколько библиотек обычно используют его (один из них - это класс String
, который вы упомянули, что вы не используете, и это хорошая точка ).
Стек и кучу можно увидеть на рисунке ниже (любезно предоставлено Adafruit ):
Когда вы выясните, как найти сгенерированный файл .elf во временном каталоге, вы можете выполнить приведенную ниже команду, чтобы сбросить использование SRAM, где project.elf
должен быть заменен сгенерированным .elf
. Преимуществом этого вывода является возможность проверки того, как используется ваша SRAM. Должны ли все переменные быть глобальными, действительно ли они нужны?
avr-objdump -S -j .bss project.elf
project.elf: file format elf32-avr
Disassembly of section .bss:
00800060 <__bss_start>:
...
00800070 <measurementReady>:
...
00800071 <cycles>:
...
00800073 <measurement>:
800073: 00 00 00 00 ....
00800077 <measurementStart>:
800077: 00 00 00 00 ....
0080007b <timerOverflows>:
80007b: 00 00 00 00
Обратите внимание, что это не показывает использование стека или динамической памяти, как отметил Игнасио Васкес-Абрамс в комментариях ниже.
Кроме того, может быть проверен avr-objdump -S -j .data project.elf
, но ни одна из моих программ не выводит ничего с этим, поэтому я не могу точно сказать, полезно ли это. Он должен перечислять 'инициализированный ( ненулевые) данные.
Я некоторое время подозревал, что добираюсь до конца доступной ОЗУ. Я не думаю, что я использую слишком много стека (хотя это возможно), что лучший способ определить, сколько оперативной памяти я фактически использую?
Лучше всего использовать комбинацию ручной оценки и с помощью оператора sizeof
. Если все ваши объявления статичны, то это должно дать вам точную картину.
Если вы используете динамические распределения, вы можете столкнуться с проблемой, как только вы начнете освобождать память. Это происходит из-за фрагментации памяти в куче.
Пройдясь и пытаясь разобраться в этом, у меня возникают проблемы, когда я перехожу к перечислениям и структурам; сколько памяти они стоят?
Перечисление занимает столько же места, сколько int
. Итак, если у вас есть набор из 10 элементов в объявлении enum
, это будет 10*sizeof(int)
. Кроме того, каждая переменная, использующая перечисление, представляет собой просто int
.
Для структур проще всего определить sizeof
, чтобы узнать. Структуры занимают (минимальное) пространство, равное сумме его членов. Если компилятор выполняет выравнивание структуры, тогда это может быть больше, однако это маловероятно в случае avr-gcc
.
Существует программа под названием Arduino Builder , которая обеспечивает аккуратную визуализацию количества вспышек, SRAM и EEPROM, которые использует ваша программа.
Конструктор Arduino является частью решения CodeBlocks Arduino IDE . Он может использоваться как автономная программа, так и через CodeBlocks Arduino IDE.
К сожалению, Arduino Builder немного старый , но он должен работать для большинства программ, и большинство Arduinos, например Uno.