Существуют ли средства для уменьшения размера кода?
- Войдите на сайт для отправки комментариев
Вс, 25/10/2015 - 16:39
Вопрос вызван тем, что после подключения очередного модуля объем кода составил 32586 байт, что немного больше, чем возможно прошить в Уно/Мини/Нано.
Кое-как удалось ужать код до 31880 байт. В основнеом за счет уменьшения длины текстовых констант, выводимых в serial в целях отладки и за счет отказа от одной из библиотек с заменой ее самописными функциями. А проект еще далек от завершения.
Мега 2560 не подходит по геометрическим размерам.
Существуют ли способы заметного уменьшеия объема кода? Например, на PC можнор было воспользоватся упаковщиком кода. Существует ли что-то подобное для Ардуино? Или, может, можно как-то поиграть с ключами компиляции?
-Os у avr-gcc
это по размеру
Если много текстовых констант надо переместить их хранение во флэш используя pgmspace.h
Точнее надо так описать константы, чтобы они не копировались при старте программы в ОЗУ. Обычно описанная константа в AVR програмируется во флэш, но при старте копируется в ОЗУ. Из за этого идёт расход как ОЗУ так и флэш - для хранения кода копирования
при использовании PROGMEM константы используются напрямую из флэш. Но программу надо переписать соответствующим образом
кроме того как уже указали - использование оптимизации по размеру. Есть правда некоторые но. При оптимизации может потребоваться дорабатывать программу.
Еще вариант - перенести код на Си в Атмел Студию. Так можно несколько килобайт сэкономить. Но если используется много чужих библиотек перенос будет стоить многих усилий
еще вариант сделать свою плату нужного размера с МК типа атмега644 на 64к или атмега128 на 128к. Я делал несколько устройств ардуино с атмега128а, очень успешно, памяти дофига
Если много текстовых констант надо переместить их хранение во флэш используя pgmspace.h
Спасибо, но это способ уменьшения расхода ОЗУ, а не уменьшения объема кода. Т.к. ОЗУ много не бывает, я его, естественно, тоже использую.
кроме того как уже указали - использование оптимизации по размеру. Есть правда некоторые но. При оптимизации может потребоваться дорабатывать программу.
Вот это интересно. Только, честно говоря, я не вижу здесь простора для работы: как для кода, иак и для данных, мне кажется, должна быть использована двухбайтовая адресация. Ну, пока я не озаботился переходом на fnvtuf64/128. Каким образом здесь можно что-нибудь сэкономить? И в чем заключается доработка программы?
Еще вариант - перенести код на Си в Атмел Студию. Так можно несколько килобайт сэкономить. Но если используется много чужих библиотек перенос будет стоить многих усилий
Боюсь не подходит. Переходить с ООП на процедурное программирование как-то не хочется.
еще вариант сделать свою плату нужного размера с МК типа атмега644 на 64к или атмега128 на 128к. Я делал несколько устройств ардуино с атмега128а, очень успешно, памяти дофига
А вот это интересная мысль, сам я как-то даже об этом не думал. И, наверное, напрасно. Хотя, там нужно будет предусмотреть кварц, стабилизатор питания и еще какие-то цепи + загрузчик в флэш. У меня есть программатор для Pro Mini, думаю, он подойдет и здесь.
Но сама идея хорошая, нужно будет ее обмозговать.
Мне видится два варианта
1. Использование "маленькой меги" http://www.ebay.com/itm/Mega2560-CORE-mini-2560-Arduino-compatible-3-3V-... размер 51х36
2. Преход на stm32 (если код переносимый) используюя проект http://www.stm32duino.com/ и брать maple mini/
Я выбрал последний варинат
Программировать порты напрямую макросами avr-gcc или через билиотеки Cyberlib к примеру. Размер скетчей падает сразу в "разы".
260 кб хватит?
https://www.pjrc.com/teensy
И с Arduino IDE совместимо, кому это критично.
Что-то разговор постепенно сместился с ужимания кода к поиску замену с бОльшим объемом памяти. Это конечно, тоже интересно, я благодарен всем откликнувшимся и внимательно изучу все предложенные варианты. Хотя несколько настораживают как цены, так и то, что большая часть предложенного, насколько я понимаю, на 3.3 В.
По поводу перевода кода с Ардуино на AVR, честно говоря, это не то, что бы хотелось.
Собственно, основной объем кода - не работа с портами, а вычисления: преобразование координат, тригонометрия...
По поводу перевода кода с Ардуино на AVR, честно говоря, это не то, что бы хотелось.
Собственно, основной объем кода - не работа с портами, а вычисления: преобразование координат, тригонометрия...
типы float и double? у AVR нет математического ядра для операций с плавающей точкой, так что каждая такая операция раскладывается в кучу целочисленных операций. Тут что ардуионо, что AVR без разницы
оптимизация компилятора по размеру может ужать код процентов на 10-15, но в некоторых случаях при такой оптимизации может измениться результат и все пойдет насмарку.
поэтому для вашего случая по-моему вариант только один - брать плату с большей памятью
Если основная проблема в большой математике, то самый очевидный путь - переход на то железо, которое для такой работы предназначено в первую очередь. Если критично применение именно атмелов, то:
а) поискать среди них что-то более подходящее. Там есть вполне себе с математикой. atmel.com вам в руки;
б) упростить математику, возможно с переходом на целочисленные, приближенные вычисления. Табличные вычисления вам в руки.
Тип float, т.к. настоящего double в Arduino все равно нет. Что аппаратно не поддерживается, я в курсе.
В свое времяы, помнится, в Турбо-Паскале был нестандартный 6-байтовый тип real, оптимизированный именно для применения в системах без x87.
Но, в общем, я пордумаю над тем, как можно упростить математику.
Боюсь, что табличные вычисления - это способ ускорения вычислений, а не сокращения объема кода. Объем как раз возрастает за счет самих таблиц. Если таблицы насчитаны заранее, кроме как в flash их хранить все равно негде. А если вычислять перед началом работы, то вычисляющий код никуда не девается.
Но по моим понятиям 16 MIPS - весьма серьезная вычислительная мощность, так что так что есть определенный спортивный интерес в том, чтобы ограничиться именно стандартным Arduino. Был даже готов обходиться 2К оперативки. Но засада подкралась с неожиданной стороны - объем кода.
Вопрос вызван тем, что после подключения очередного модуля объем кода составил 32586 байт, что немного больше, чем возможно прошить в Уно/Мини/Нано.
Кое-как удалось ужать код до 31880 байт. В основнеом за счет уменьшения длины текстовых констант, выводимых в serial в целях отладки и за счет отказа от одной из библиотек с заменой ее самописными функциями. А проект еще далек от завершения.
Мега 2560 не подходит по геометрическим размерам.
Существуют ли способы заметного уменьшеия объема кода? Например, на PC можнор было воспользоватся упаковщиком кода. Существует ли что-то подобное для Ардуино? Или, может, можно как-то поиграть с ключами компиляции?
Экземпляр класса Serial жрет около 10% памяти в UNO. А вместе с вашими строками, выводящимися в порт - можно и до 20% дойти. Вывод логичный - сделайте прототип для отладки на MEGA, а затем просто уберите Serial и все, что с ним связано и загрузите в UNO.
Размер таблиц существенно зависит от требуемой точности вычислений и их характера. И если вам не требуется высокая точность, то за счет загрубления таблиц можно сэкономить (и порой существенно) на размере подсоединяемых библиотек вещественной арифметики и кода их использующего. Не забывайте, что каждая передача длинного параметра растит вызов на 6 байт а для float и поболее.
К примеру, мне необходимо вычислять синусы с приемлемой точностью для узв. датчика расстояний с круговым обзором. НО! Я знаю, что он вертится на оси с "шагом" и его разрешающая способность у меня в районе 6 градусов .. то есть "на круг" мне требуются рассчеты всего по 60 значениям. Зачем мне вся таблица Брадиса? Насчитать 60 синусов и положить их в прогмем даже по 8 байт = 480 байт программной памяти. Уверен, что только функция синуса займет столько или больше. А ещё ей ведь надо передавать параметром 8 байт! И при каждом вызове. А так, я работаю с .. адресом и смещением в таблице. Адрес - 2 байта и он статичен и известен при компиляции, а смещение - ваще одного байта хватит. И точность - та, которую я положил в таблицу. Какая требуется, ту и запихнул.
Я про эту сторону табличных вычислений. Ну и скорость конечно же.
А так, да. Если вы пользуете типовые библиотеки, да ещё и на классах - ожидать небольших размеров от программы - наивно. Тот же сериал в киберлибе - с десяток команд на функцию передачи .. все таки советую ознакомится с прямым программированием AVR через их макросы в io.h и прочих.
Я согласен с предыдущим. Делайте отладку на более мощном процессоре, а потом отключайте вывод в консоль. Делается просто - создается дефайн и при выводе делается проверка дефайна.
#define debug
--код
#ifdef debug
-- serial....
#endif
Экземпляр класса Serial жрет около 10% памяти в UNO. А вместе с вашими строками, выводящимися в порт - можно и до 20% дойти. Вывод логичный - сделайте прототип для отладки на MEGA, а затем просто уберите Serial и все, что с ним связано и загрузите в UNO.
Спасибо.
В принципе меня такие мысли уже посещали (прототип на Меге), но проблема в том, что прототипа как такового не существует. Ну, возможно, точнее: изготовление финального изделия не предполагается. Проект исследовательский и в какой-то степени нацелен на выяснение, что можно выжать из Arduino.
И геометрические ограничения связаны не с особенностями дизайна, а с тем, что все это должно ездить на двух колесиках, не задевая предметы обстановки. Мега в габариты не вписывается.
По поводу отключения COM-порта тоже думал, благо есть собственный дисплей. Равно как и думал о вырезании из библиотеки дисплея поддержки кириллицы.
Пооптимизировать код ручками, именно в таком порядке.
1. Типы данных проверить, нет ли избыточности, где не нужен int хватит и byte и т.д.
2. Рассмотреть оптимальность разделения на функции, убрать "обертки", функции вызываемые только раз в inline.
3. Рассмотреть параметры функций и уменшить их количества путем перехода на использование глобальных переменных, упаковки группы булевских в битовые поля, обединения в структуры и передачи указателей на них и т.д. Важен индивидуальный подход к каждой ситуаци, постоянно контролировать размер после каждого изменения.
4. Снова рассмотреть оптимальность разделения на функции. Теперь на предмет обединения кусков кода из разных мест в одну функцию, возможна с параметром. Довольно типичный пример - функция удаляет файлы соответствующие заданым сложным условиям, аналогичная функция переименовывает файлы соответствующие заданым сложным условиям, следующая - отправляет их куда-то. Вместо трех этих пишем одну с параметром определяющим действие.
Вообще проще сразу старатся писать оптимально, постоянно поглядывая на размер, чтоб возникало понимание того, что "стоит" то или иное действие.
Если не влазит при включении отладки, например через Serial, можно и по частям поотлаживать.
каждая передача длинного параметра растит вызов на 6 байт а для float и поболее.
Честно говоря, это непонятно. Как-то привык на PC, что переменная занимает в стеке ровно столько, сколько соответствует ее размеру. На AVR это не так?
В чем тут логика? Казалось бы, ограниченность ресурсов должна диктовать экономное использование стека.
60 синусов и положить их в прогмем даже по 8 байт = 480 байт программной памяти. Уверен, что только функция синуса займет столько или больше.
Проверил: синус - 300 байтов. Правда, преобразование float в строку (для вывода в COM) - почти 2 Кбайта. Повторный вызов синуса добавляет к коду 8 байт. Если вместо второго синуса вызвать косинус + 14 байт.
А так, да. Если вы пользуете типовые библиотеки, да ещё и на классах - ожидать небольших размеров от программы - наивно. Тот же сериал в киберлибе - с десяток команд на функцию передачи .. все таки советую ознакомится с прямым программированием AVR через их макросы в io.h и прочих.
Честно говоря, киберлиб смотрел (глазами - в коде не щупал). На первый взгляд он мне не понравился. Но посмотрю еще раз. В конце концов, если на универсальную библиотеку он не тянет, то, возможно, можно использовать какую-то специализированную часть.
Заманчиво, конечно, вообще отказаться от COM-порта, но как выдавать результаты на комп?
Пооптимизировать код ручками, именно в таком порядке.
...
Вообще проще сразу старатся писать оптимально, постоянно поглядывая на размер, чтоб возникало понимание того, что "стоит" то или иное действие.
Если не влазит при включении отладки, например через Serial, можно и по частям поотлаживать.
За советы спаситбо.
Честоно говоря, всегда стараюсь писать оптимально, правда, под "оптимально" всегда подразумевал скорость вычислений и используемый размер оперативной памяти вообще и стека в частности. А вот навыков оптимизации по размеру кода не приобрел. Разве что очень давно и на Ассемблере 8080. Но там было просто - каждая инструкция занимает от 1 до 3 байт. Да и оптимизировать приходилось очень маленткие фрагменты, как правило, не более 128 байт.
Ну а здесь, наверное, действительно придется по частям.
Но вообще, это геморрой:
- заливаем скетч,
- пишем данные в EEPROM,
- заливаем другой скетч,
- из EEPROM выводим в Serial,
и так раз пятнадцать для накопления необходимой статистики.
Сцществует. Писать на чистом C/C++ для AVR без использования ядра arduino, вы просто не поверите своим глазам, когда скомпилите код и он будет в 2 и больше раз меньше ардуиновского.
Кстати здесь приведены некоторые примеры замена ардинокода на чистый АВР
http://arduino.ru/forum/programmirovanie/attiny13a-101-primenenie
В чем тут логика? Казалось бы, ограниченность ресурсов должна диктовать экономное использование стека.
Читайте соглашения компиляции avr-gss от glibc. Там черным по желтому писано, что по возможности, параметры передаются в регистрах, а не на стеке. А вот теперь, вам "вопрос на засыпку": сколько потребуется команд для загрузки длинного целого в регистр? Верно - ровно 4шт. Ибо проц работает исключительно с байтами. Да, не забудьте, что все команды тянут за собой ещё и адрес памяти. Передача через стек будет занимать БОЛЬШЕ места, поскольку сначала надо чиселко загрузить в регистр, а тока апосля запиховывать его в стек.
Ну вот я примерно про эти соотношения. float хорош ровно там, где его считать умеют. А ежели железяка не предназначена, то и нефиг над ней извращаться. Без него все ровно также можно считать. Да, там ещё как обязаловка - обратная операция: преобразование строки во флоат.. :)
Ну смотрите тогда мой вариант, но он исключительно для Мега2560, распиновку под другую машинку можете сделать самостоятельно. Там ничего сложного нет. Достоинство по отношению к Cyberlib - это предоставление работы в терминах Wiring, но напрямую через макросы из io.h и др. То есть, в проге можете прямо писать digitalWrite() и это будет занимать 2 байта кода, а не 400. Где-то тут, в разделе "Программирование" выкладывал ссыль. :)
Что-то разговор постепенно сместился с ужимания кода к поиску замену с бОльшим объемом памяти. Это конечно, тоже интересно, я благодарен всем откликнувшимся и внимательно изучу все предложенные варианты. Хотя несколько настораживают как цены, так и то, что большая часть предложенного, насколько я понимаю, на 3.3 В.
По поводу перевода кода с Ардуино на AVR, честно говоря, это не то, что бы хотелось.
Собственно, основной объем кода - не работа с портами, а вычисления: преобразование координат, тригонометрия...
ИМХО Собственно весь смысл ардуино в простоте. Что-то типа брать штампы готовых слов, а не писать каждую букву отдельно.
Если происходят вычисления с большой точностью, а результат не очень большой, то можно использовать двойное «преобразование типов данных» т.е. увеличивать размер для вычислений а потом его уменьшать. Не плодить локальные переменные, а аккуратно использовать глобальные и т.п.
Можно, конечно, на асме написать, но это уже далеко не ардуино будет….
ЗЫ что-то это уже написано многими, только более подробно
объем доступной памяти можно увеличить отказавшись от загрузчика и перенеся все константы в еепром
объем озу можно увеличить добавив память напр микросхемами серии 23, поддерижвают i2c, но медленнее родной озу.
или переходить на другой мк.
объем кода можно сократить отказавшись от ООП и выкинув ненужные либы.
объем кода можно сократить отказавшись от ООП ....
.... выкинув ненужные либы.
Проект исследовательский и в какой-то степени нацелен на выяснение, что можно выжать из Arduino.
Такая постановка вопроса тянет на полноценную монографию, переходящую в ПСС :) Там должно быть отражено поистине безмерное количество вариантов применения, где реализуются те или иные возможности МК.
Я бы в принципе "перевернул" задачу - насколько сложный проект можно реализовать на Ардуино? Из не банального, но вполне конкретного проекта будут следовать и пути его осуществления, наиболее полно раскрывающие возможности платформы. Ардуино же не сама по себе, а "для чего то". Ответ на вопрос "что может Ардуино" интересен только в продолжении "для чего ее тогда можно применить?". И здесь, на мой взгляд, существенно важнее глубокое понимание задачи, предметной области, чем МК. Когда Вы точно знаете, что нужно, на чем можно экономить, а на чем - нет, то и быстро найдете способы выжать требуемое из МК. Например. "Удар монтировкой заменяет 30 минут ППР." "Два резистора и два конденсатора заменяют цифровой ФНЧ второго порядка." ...
Отчасти это подтверждают ответы в теме, предлагающие совершенно различные способы получения экономного кода. Но нигде не было предложено универсального "ужимальщика", аналогичного архиватору, сохраняющему полную совместимость с Arduino IDE.
И геометрические ограничения связаны не с особенностями дизайна, а с тем, что все это должно ездить на двух колесиках, не задевая предметы обстановки. Мега в габариты не вписывается.
Т.е. проект то все-таки прикладной, причем к колесикам конечного размера. :)
По поводу отключения COM-порта тоже думал, благо есть собственный дисплей. Равно как и думал о вырезании из библиотеки дисплея поддержки кириллицы.
У меня, наверное по привыке, как то так случилось, что всю отладку при наличии дисплея я веду через него и без особого напряга. Единственный у меня дисплей, имеющий кириллицу, МЭЛТ не заговорил по-русски ни с одной библиотекой "а ля рюс". Вместо них я предварительно руками кодирую русские текстовые сообщения. И тоже особо не напрягает.
Поддержу полностью. Без понимания задачи, рассуждать по оптимизации кода - можно только в терминах "сферического коня в вакууме". А озвученная постановка ".. что можно выжать из" - это да, ПСС. Причем многотомный, ибо применений масса.
Но вот кстати, требование "вычислять флоат" - как раз из разряда "не может". К задаче "выжать из" не имеет никакого отношения. :)
Ну и непонятны требования к габаритам "мега не вписывается" .. в катающуюся тележку на колесиках?!? Легко. Вписывается вместе с моторами, колесиками от Лего (большими) и батарейным отсеком в размер 17х17х15см - легко.
У меня 12. И это диаметр.
Это первое.
Без плавающей точки не получается. Нет, в принципе, можно было бы посчитать и в фиксированной. Только не думаю, что это будет менее ресурсоемко.
Это второе.
Собственно сама задача описана здесь: http://arduino.ru/forum/proekty/robot-kartograf
Поводом для размещения настоящей темы послужил тот факт, что процедура юстировки компаса заняла последние 7 кбайт отведенной для программ памяти. Один оборот вокруг своей оси и 2.3 секунды вычислений.
Вот теперьт чешу репу, что делать дальше.
Тут в соседнем разделе обсуждается самоперепрошивка Ардуино файлами с SD.
Кто-нибудь подобное делал?
Какой примерно баланс памяти?
Насколько это может быть применимо для "упихивания" всего необходимого в Uno?