Я использую слишком много ОЗУ. Как это можно измерить?

Я хотел бы знать, сколько оперативной памяти, которую я использую в своем проекте, насколько я могу судить, нет никакого способа на самом деле справиться с этим (за исключением того, что я сам и сам его вычисляю). У меня есть сцена в довольно большом проекте, где я определил, что у меня заканчивается 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.

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

19 голосов | спросил Madivad 16 MarpmSun, 16 Mar 2014 19:00:54 +04002014-03-16T19:00:54+04:0007 2014, 19:00:54

5 ответов


16

Вы можете использовать предоставленные функции 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);
}
ответил alexan_e 17 MaramMon, 17 Mar 2014 03:29:39 +04002014-03-17T03:29:39+04:0003 2014, 03:29:39
10

Основные проблемы, которые могут возникнуть при использовании памяти во время выполнения, следующие:

  • Отсутствует доступная память в куче для динамических распределений (malloc или new)
  • при вызове функции
  • не осталось места в стек

Оба на самом деле такие же, как AVR SRAM (2K на Arduino) используется для обоих (помимо статических данных , размер которых никогда не изменяется во время выполнения программы).

Как правило, динамическое распределение памяти редко используется на MCU, только несколько библиотек обычно используют его (один из них - это класс String, который вы упомянули, что вы не используете, и это хорошая точка ).

Стек и кучу можно увидеть на рисунке ниже (любезно предоставлено Adafruit ): введите описание изображения здесь>> </p>

<p> Следовательно, наиболее ожидаемая проблема возникает из-за переполнения стека (т.е. когда стек растет в сторону кучи и переполняется на нем, а затем - если куча не использовалась во всех переполнениях в зоне статических данных SRAM. в то время у вас есть высокий риск: </p>

<ul>
<li> Повреждение данных (т. е. стек выполняет кучу или статические данные), что дает вам непонятное поведение </li>
<li> повреждение стека (т. е. куча или статические данные перезаписывают содержимое стека), что обычно приводит к сбою </li>
</ul>
<p> Чтобы узнать объем памяти, оставшийся между вершиной кучи и вершиной стека (на самом деле, мы можем назвать ее нижней, если представить как кучу, так и стек на том же изображении, что изображено ниже ), вы можете использовать следующую функцию: </p>

<pre><code>int freeRam () {
  extern int __heap_start, * __ brkval;
  на телевидении;
  return (int) & v - (__brkval == 0? (int) & __ heap_start: (int) __brkval);
}
</code></pre>

<p> В приведенном выше коде <code>__ brkval</code> указывает на вершину кучи, но является <code>0</code>, когда куча не была использована, и в этом случае мы используем <code>& amp ; __ heap_start</code>, который указывает на <code>__ heap_start</code>, первую переменную, которая отмечает нижнюю часть кучи; <code>& v</code> в верхней части стека (это последняя переменная, помещенная в стек), поэтому приведенная выше формула возвращает объем доступной памяти для стека (или кучу, если вы используйте его), чтобы расти. </p>

<p> Вы можете использовать эту функцию в разных местах вашего кода, чтобы попытаться выяснить, где этот размер значительно сокращается. </p>

<p> Конечно, если вы увидите, что эта функция возвращает отрицательное число, то уже слишком поздно: вы уже переполняете стек! </p></body></html>

ответил alexan_e 17 MaramMon, 17 Mar 2014 03:29:39 +04002014-03-17T03:29:39+04:0003 2014, 03:29:39
7

Когда вы выясните, как найти сгенерированный файл .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, но ни одна из моих программ не выводит ничего с этим, поэтому я не могу точно сказать, полезно ли это. Он должен перечислять 'инициализированный ( ненулевые) данные.

ответил jippie 17 MaramMon, 17 Mar 2014 00:31:20 +04002014-03-17T00:31:20+04:0012 2014, 00:31:20
3
  

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

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

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

  

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

Перечисление занимает столько же места, сколько int. Итак, если у вас есть набор из 10 элементов в объявлении enum, это будет 10*sizeof(int). Кроме того, каждая переменная, использующая перечисление, представляет собой просто int.

Для структур проще всего определить sizeof, чтобы узнать. Структуры занимают (минимальное) пространство, равное сумме его членов. Если компилятор выполняет выравнивание структуры, тогда это может быть больше, однако это маловероятно в случае avr-gcc.

ответил asheeshr 16 MarpmSun, 16 Mar 2014 19:15:03 +04002014-03-16T19:15:03+04:0007 2014, 19:15:03
1

Существует программа под названием Arduino Builder , которая обеспечивает аккуратную визуализацию количества вспышек, SRAM и EEPROM, которые использует ваша программа.

 Arduino Builder

Конструктор Arduino является частью решения CodeBlocks Arduino IDE . Он может использоваться как автономная программа, так и через CodeBlocks Arduino IDE.

К сожалению, Arduino Builder немного старый , но он должен работать для большинства программ, и большинство Arduinos, например Uno.

ответил sa_leinad 14 J0000006Europe/Moscow 2018, 16:40:13

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132