Официальный сайт компании Arduino по адресу arduino.cc
Удивительное рядом - оптимизация кода компилятором и класс HardwareSerial
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Возьмем пустой скетч. Откомпилируем:
void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: } Скетч использует 444 байт (1%) памяти устройства. Глобальные переменные используют 9 байт (0%) динамической памяти,
Сразу непонятно, куда девались 9 байт, если в скетче нет переменных. Пустячок, но неприятно.
Добавим некую функцию:
void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: } int b707(void) { int a,b,c; a++; b=c+a; } Скетч использует 444 байт (1%) памяти устройства. Глобальные переменные используют 9 байт (0%) динамической памяти
Как видим, ни размер кода, но использование памяти не изменилось, что совершенно логично. Наша функция определена, но нигде в программе не используется - поэтому компилятор ее выкинул из конечного кода.
Добавим вывод:
void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: } int b707(void) { int a,b,c; a++; b=c+a; Serial.print(a); } Скетч использует 1284 байт (3%) памяти устройства. Глобальные переменные используют 182 байт (8%) динамической памяти
Опа! Функция b707() по прежнему в программе не используется - но класс Serial подгружается и отжирает свои 1к во флеше и 170 байт оперативы (8%) !!!
Более того, если код функции
int b707(void) { int a,b,c; a++; b=c+a; Serial.print(a); }
вырезать из скетча, сохранить во внешний файл c расширением.cpp и ПРОСТО ПОЛОЖИТЬ в одну папку со скетчем - даже не добавляя никаких #include - он все равно будет подгружен компилятором, попадет в бинарный код и отожрет столько же памяти, как в предыдущем случае.
Вопрос к гуру - почему вызовы Serial.print, даже расположенные в неиспользуемых функциях, не выпиливаются оптимизатором из кода? Относится ли это ко всем классам или класс Serial особенный?
Практические выводы:
1. Если в скетче не хватает памяти и в конечном коде вам не нужен дианостический вывод в монитор - физически удаляйте все вызовы класса Serial, в том числе из неиспользуемых функций. Подключайте и отключайте диагностический вывод при поиощи директив условной компиляции #ifdef
2. (в принципе, это известно - но нелишне напомнить) Будьте внимательны к тому, какие файлы лежат в каталоге программы. Ардуино IDE подключает при компиляции все .cpp файлы из каталога скетча не спрашивая.
Да, забыл указать - использовалась Ардуино IDE 1.6.12, Win7 x64
Интересно было бы проверить, как себя ведут в этой ситуации другие компиляторы
Также ведут (ну там +/- по результатам оптимизации). В целом это правильное и документированное поведение.
Также ведут (ну там +/- по результатам оптимизации). В целом это правильное и документированное поведение.
Правильное? Что же правильного в том. что класс, к которому нет обращений в коде - занимает место в памяти?
Ну это как бы типа сродни обработчикам прерываний. Может ли компиль самостоятельно их выкинуть? На основании чего?
Правильное? Что же правильного в том. что класс, к которому нет обращений в коде - занимает место в памяти?
А Вы исследуйте вопрос поглубже, а именно - посмотрите, что именно он не выбрасывает. Убедитесь, что все там правильно.
Только внимательнее относитесь к делу, а то некоторые Ваши оговорки просто пугают. Вот, Вы пишете
Как видим, ни размер кода, но использование памяти не изменилось, что совершенно логично. Наша функция определена, но нигде в программе не используется - поэтому компилятор ее выкинул из конечного кода.
Уверен, что это именно оговорка, т.к. Вы, разумеется, понимаете, что компилятор никак не мог выбросить функцию, которая не объявлена как static, т.к. компилятро имеет дело с одним файлом и ему просто неоткуда узнать, что эта функция не вызывается не из какого-то другого. Стало быть выбросил эту Вашу функцию не компилятор, а линкер.
Линкер же не знает многого из того, что знает компилятор. В общем, Вы аккуратно, внимательно отследите что именно он оставляет в памяти и поймёте, почему он не смог этого выбросить. Это само по себе интересное исследование.
Какой такой пустой скеч? Вы ж на си пишете. Если от вас скрывают, то это не значит что нет функции main, а в ней нет функции init. Попробуйте скомпилировать скеч в котором объявлена одна пустая main.
Практические выводы:
1. Если в скетче не хватает памяти и в конечном коде вам не нужен дианостический вывод в монитор - физически удаляйте все вызовы класса Serial, в том числе из неиспользуемых функций. Подключайте и отключайте диагностический вывод при поиощи директив условной компиляции #ifdef
ок. т.е, если "вам не нужен диагностический вывод в монитор", то процесс ненаписания вызовов класса Serial в настоящем можно приравнять к процессу физического удаления всех вызовов класса Serial в будущем.
ещё раз для понимания сути высоты глубины логического дна рекомендации:
пример: Блинк!
как вы думаете, почему скетч испольует 9 байт оперативки?
ну, это же очевидно! авторы скетча следовали рекомендации №1 b707.
а, не потому, что вы подумали...
ок. т.е, если "вам не нужен диагностический вывод в монитор", то процесс ненаписания вызовов класса Serial в настоящем можно приравнять к процессу физического удаления всех вызовов класса Serial в будущем.
Очень верно подмечено. Не одному ж тебе велосипеды изобретать :)
Очень верно подмечено. Не одному ж тебе велосипеды изобретать.
вообще то - это был сарказм.
колись, зачем пишешь в скетч неиспользуемые функции?
вообще то - это был сарказм.
Клапа, я догадался :) А мой ответ - самоирония.
колись, зачем пишешь в скетч неиспользуемые функции?
в скетч лишние функции и правда писать не стоит, но ведь с функциями из подключаемых библиотек все обстоит так же.
но ведь с функциями из подключаемых библиотек все обстоит так же.
абсолютно так же - не пиши в библиотеки неиспользуемые функции.