Понимание глобальных переменных на Arduino
Я переписываю набор функций манипулирования светодиодами, чтобы они были более объектно-ориентированными, и обнаружил, что каким-то загадочным образом пространство моей памяти для глобальных переменных было перегружено, несмотря на сокращение использования глобальных переменных.
Я поигрался с множеством вещей и нашел кое-что любопытное.
Вот фрагмент функции, которая не находится в объекте и вызывается напрямую из loop()
:
Animation *getNewAnim(int index)
{
switch(index)
{
case 0:
return new RedClouds();
case 1:
return new TwoLayerClouds();
case 2:
return new LavaLamp1();
case 3:
return new Bubbles1();
... (20 or so more cases)
}
Это функция, которая позволяет мне переключаться между режимами отображения на основе значения index
. В данный момент времени создается только один из этих объектов; Я удаляю старый перед вызовом этого, поэтому одновременно не будет использоваться несколько экземпляров.
Любопытно то, что если я удалю несколько из этих операторов возврата, блок свернётся примерно так:
Animation *getNewAnim(int index)
{
switch(index)
{
case 0:
case 1:
case 2:
case 3:
return new Bubbles1();
}
Объем используемой глобальной памяти значительно снижается.
Моя гипотеза заключается в том, что среда сборки Arduino предварительно выделяет место для каждого из этих объектов в глобальной памяти еще до запуска программы, а не делает это динамически по мере необходимости.
Правильна ли моя гипотеза? И если да, то есть ли способ сказать среде сборки не делать этого, поскольку я предпочитаю использовать для моего нового объекта ту же память, которая использовалась старым.
Для пояснения: при сборке я вижу такой результат:
Sketch uses 6,550 bytes (21.3%) of program storage space. Maximum is 30,720 bytes.
Global variables use 1,167 bytes (57.0%) of dynamic memory, leaving 881 bytes for local variables. Maximum is 2,048 bytes.
Когда я говорю о «глобальной памяти», я имею в виду количество глобальных переменных на этом выходе. Также отмечу, что при компиляции второго примера объем памяти программы также резко уменьшается.
НОВАЯ ГИПОТЕЗА
Вот что, по моему мнению, может происходить. Когда я опускаю классы в операторе переключения, компилятор, кажется, оптимизирует их из кодовой базы, чего я и ожидаю, и это соответствует уменьшению объема памяти программы. У каждого класса есть метод, который возвращает его имя. Этот метод содержит встроенную строку, и теперь я считаю, что эта строка хранится в пространстве глобальных переменных. Следовательно, если класс включен, для хранения имени используется больше пространства глобальных переменных, а если оно оптимизировано, используется меньше. Эта теория согласуется со всеми имеющимися у меня фактами и имеет больше смысла, чем моя предыдущая гипотеза.
@watusimoto, 👍6
Обсуждение1 ответ
Я предлагаю вам взглянуть на https://stackoverflow.com/questions/16274451/operator- new-for-arduino оператор new взят не из библиотек C++, он взят из библиотек Arduino. Это оболочка для malloc. Использование malloc() и free() - плохая идея для Arduino?
- Почему считается плохой практикой использовать ключевое слово "new" в Arduino?
- Использование памяти: #define против static const для uint8_t
- Есть ли ограничения на размер массива в Arduino Mega 2560?
- Считывание байтов из массива PROGMEM
- Нужно ли удалять переменные перед сном?
- Как уменьшить использование глобальных переменных? Attiny85
- deserializeJson() не удалось: NoMemory при отправке последовательного json с использованием ArduinoJson
- Проблема с переменной char* malloc/free. Пустое содержимое в переменной получателя после использования free
Вы имеете в виду PROGMEM или RAM? Пожалуйста, опубликуйте [Минимальный, полный и проверяемый пример](http://stackoverflow.com/help/mcve). Я не понимаю, как «среда сборки» (в основном g++) может предварительно выделить пространство для объектов, которые в любом случае будут распределяться динамически. Как вы измеряете «глобальное пространство памяти» и что именно вы под этим подразумеваете?, @Nick Gammon
return new Bubbles1();
- зачем скобки? Почему бы и нет:вернуть новые пузырьки1;
?, @Nick Gammon@Ник: Я добавил немного внизу своего вопроса, который, надеюсь, прояснит ситуацию. Что касается ()... хороший вопрос. Я посмотрю на это., @watusimoto
Не видя остальную часть вашего кода, а только [фрагмент](http://snippets-r-us.com/), трудно сказать. Возможно, другие классы имеют статические переменные. Если компилятор никогда не видит их создания, он может пропустить статические переменные., @Nick Gammon
Сокращение памяти программы имеет смысл; если экземпляр класса никогда не создается, компоновщик, вероятно, не будет включать код. Однако я попробовал добавить больше статики как в класс, так и в некоторые его методы, и это вообще не повлияло на глобальное пространство памяти (чего я и ожидал). Если мне удастся решить несвязанную проблему и убедиться, что с лицензией все в порядке, я, вероятно, смогу загрузить все это на github., @watusimoto
Люди часто спрашивают: «Почему программа становится намного больше, когда я **вызываю** функцию?». С их точки зрения, они добавили всего одну строку кода. С точки зрения компилятора, вещи, которые были вызваны, теперь необходимы, тогда как раньше они не были нужны., @Nick Gammon
Я должен отметить, что это немного выдуманная проблема. Вы спрашиваете: «Если я опускаю какой-то код (чего на практике я не могу), почему исполняемый файл становится меньше и использует меньше оперативной памяти?». Это гипотетическая проблема. Вы *не* пропустите код. И в компиляторе нет переключателя типа «не тратить память». Это уже высокооптимизирующий компилятор. Он склонен делать вещи, которые выглядят странно, если вы этого не осознаете., @Nick Gammon
В каком-то смысле это теоретически, но если я смогу понять, почему происходит то, что я вижу, я смогу спроектировать эту проблему. На самом деле я просто догадываюсь о том, что происходит в компиляторе, и, как вы говорите, он не всегда ведет себя так, как ожидалось. Что мне нужно, так это инструмент, который сообщал бы мне, что компилятор помещает в пространство глобальных переменных., @watusimoto
Класс Animation определяет ряд виртуальных функций-членов. Компоновщику сложно удалить виртуальные функции-члены, которые не используются/вызываются, поскольку в виртуальной таблице есть ссылка. Если функции используют строковые литералы, все они размещаются в «глобальных переменных». Вот почему важно использовать макрос F()., @Mikael Patel
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_flashstrings, @Mikael Patel
см. также http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_varinit., @Mikael Patel
Попробуйте запустить
avr-nm -Crtd --size-sort
для вашего файла elf. Ищите символы типа «D» (инициализированные данные), «B» (BSS) и «V» (vtable) в верхнем или нижнем регистре. Полученный список отсортирован по размеру. Литеральные строки могут отсутствовать в этом списке, но вы увидите их с помощьюavr-objdump -s -j .data
., @Edgar Bonet