Должен ли оптимизатор выкидывать из конечного кода неиспользуемые методы класса?
- Войдите на сайт для отправки комментариев
Столкнулся с такой ситуацией - беру пустой код ардуино, добавляю туда экземпляр своего класса - больше ничего. Никакого кода с участием этого экземпляра нет, только обьявление - но итоговая прошивка сразу увеличивается на 8к.
В связи с этим два вопроса:
1. разве по умолчанию оптимизатор не должен выкидывать из кода неиспользуемые методы класса?
2. если по умолчанию нет - есть ли какие-то способы заставить его это делать? Или не заставить - а скорее помочь определить, что данный код лишний и может быть выкинут....
Понятно, что задача с полностью лишним классом - надуманная. Но ситуации, когда из огромной библиотеки используется лишь пара методов -они же сплошь и рядом. я всегда думал. что это не страшно. мол все лишнее оптимизатор выкинет сам. Но тут весь класс лишний - а ничего не выкидывается.
Подпишусь
рассуждая дальше....
А с чего я взял, что ничего не выкидывается? - может, если бы не выкинулось, код бы вырос на 16к. А так всего на 8 - значит оптимизатор что мог - выкинул...
Интересно, это правильное предположение? И как все-таки помочь оптимизатору выкинуть побольше?
Для начала, видимо, выяснить, а есть ли там вообще что выкидывать
каким образом?
А с чего я взял, что ничего не выкидывается? - может, если бы не выкинулось, код бы вырос на 16к. А так всего на 8 - значит оптимизатор что мог - выкинул...
Интересно, это правильное предположение? И как все-таки помочь оптимизатору выкинуть побольше?
А в листинге что, в мап-файле?
Вот тут я хз ))
Помнится, я экспериментировал со своей кнопочной библиотекой. Вроде бы создание одного экземпляра без единого вызова его методов увеличивало код на положенные 20 байт - ровно сколько он и весит
v258 20 байт в памяти переменных видимо, тут речь про память под код
Листинг надо бы изучить и с уровнем оптимизации ничего не химичили ???
Оптимизатор не должен. Это делает компоновщик.
Никакого кода с участием этого экземпляра нет, только обьявление
Это уже минимум конструктор, деструктор, таблица виртуальных методов, и всё, от чего они зависят.
v258 20 байт в памяти переменных видимо, тут речь про память под код
Листинг надо бы изучить и с уровнем оптимизации ничего не химичили ???
Уже не помню, нужно вечером проверить ))
с уровнем оптимизации ничего не химичили ???
химичил, конечно. Самый лучший результат с -0s. с остальными уровнями размер еще больше
А код то будет ? Или так и будем гипотетически трещать ? Петрович - выходи !!!
Никакого кода с участием этого экземпляра нет, только обьявление
Это уже минимум конструктор, деструктор, таблица виртуальных методов, и всё, от чего они зависят.
виртуальных методов нет, статических тоже, класс ничему явно не наследует. Если конструктор полностью вычистить, размер занимаемого кода уменьшается незначительно.
Вообще такое впечатление, что компиляция класса подтягивает кучу системного кода ардуино. ну как если бы я использовал float - одно его упоминание в скетче увеливает размер на несколько кб. Я только не пойму. почему компоновщик его не выкидывает на этапе сборки.
Просто посмотрите на размер кода современных приложений на ББ - раньше у первых ББ размер жесткого диска был меньше чем некоторые современные программы и эти ББ при этом работали и содержали большое количество приложений.
Вся это оптимизация - фикция на бумаге в основном !!!
А код то будет ? Или так и будем гипотетически трещать ? Петрович - выходи !!!
а надо? :)))))
Класс - вот https://github.com/board707/DMD_STM32/tree/old-V1
Для определенности специально взял старую ветку проекта, потому что в ней еще не было никаких "наворотов" - ни наследования, ни виртуальных функций, ни шаблонов...
Код скетча чуть позже выложу. но он простеший. кроме создания экземпляра нет ничего.
Ассемблер наше все !
Полный USB стек для COM порта на stm32 чуть больше 1000 байт.
Ассемблер наше все !
Полный USB стек для COM порта на stm32 чуть больше 1000 байт.
зачем мне USBстек. листинг я и без него могу посмотреть. И дизассемблер тоже. Знать бы что в нем искать... это ж будет килобайт сто-двести. наверно
Ну если имена/реализация методов есть - значит ничего не обрезалось
Ну если имена/реализация методов есть - значит ничего не обрезалось
ок, гляну.
Но вопрос то был - почему так :) И как исправить это, если можно
Если конструктор полностью вычистить, размер занимаемого кода уменьшается незначительно.
Я только не пойму. почему компоновщик его не выкидывает на этапе сборки.
Конструктор инициализирует всех членов класса, то бишь всё равно вызывает кучу других конструкторов.
Не выкидывает потому что код float на avr запрограммирован на ассемблере и компиляции на уровне отдельных функций не подлежит.
код
В таком виде размер прошивки 12 624 байт, если раскомментировать 9 строчку - 20708 байт.
Аддон Кларка, плата STM32F103C8
Ссылка на класс та же, что в сообщении 14
Листинг надо бы изучить и с уровнем оптимизации ничего не химичили ???
посмотрел мап файл - легче не стало, практически все методы класса там перечислены... это означает, что все они попали в конечный код? Ну как так-то?
У мен нету вот этого всего.
Аддон Кларка, плата STM32F103C8
Так, что я только потеоретизировать могу.
А, кстати, как там насчёт LTO? Включено? Нет?
А, кстати, как там насчёт LTO? Включено? Нет?
выключено. При попытке включения сыпятся ошибки линковки.
У мен нету вот этого всего.
Аддон Кларка, плата STM32F103C8
плата для сборки скетча не нужна, аддон вот https://github.com/rogerclarkmelbourne/Arduino_STM32
просто скачать и положить в каталог hardware установки Ардуино ИДЕ (но надо чтобы была установлена поддержка Дуе)
Это так, на случай если вдруг будет скучно и захотите попробовать :)
b707, а какой размер получается если начать 9-ю строку cловом static?
b707, а какой размер получается если начать 9-ю строку cловом static?
такой же, 20708.
Да и с чего ему менятся?
выключено. При попытке включения сыпятся ошибки линковки.
Вот с этого надо начинать
Пока не разберётесь, что там с линковкой, не будет понятно куда уходит память.
Вот с этого надо начинать
Пока не разберётесь, что там с линковкой, не будет понятно куда уходит память.
убрал ошибку линковки, собираю с -flto, стало 18656 байт. Уже хорошо, но все равно неиспользуемый класс отьедает 6к
Ну, надо смотреть что за класс. Может это и нормально, а может и нет, тут без самого класса не поймёшь. Экземпляр же объявлен, так? Значит конструктор, деструктор, аллокаторы/деаллокаторы, если есть. Что там за поля у него. Если нетривиальные, то опять же конструктор/деструктор. Что там со статическими методами. В общем, это реально смотреть надо.
Ну, надо смотреть что за класс.
класс на гитхабе, если будет время
https://github.com/board707/DMD_STM32/blob/old-V1/DMD_STM32.h
https://github.com/board707/DMD_STM32/blob/old-V1/DMD_STM32.cpp
Я понять не могу - у тебя в конструкторе много значений, а вроде выше используется только одно (значений «по умолчанию» я не увидел), как так? Что я пропустил?
Я понять не могу - у тебя в конструкторе много значений, а вроде выше используется только одно (значений «по умолчанию» я не увидел), как так? Что я пропустил?
строчку 8 и 9 не спутал?
Ну да, спутал )))
Может это поддержка spi так объемно подтягивается из аддона ?
расшифровка размера бинарника по секциям ( вывод программы arm-none-eabi-size):
С моим классом
Без него:
Видно, что в моем классе нет статических переменных.
Отмечу, и тот и другой код компилированы с -flto опцией. Вчера я этого не заметил, но как следует из цифр - LTO никак не влияет на вклад моего класса в размер кода - после включения LTO общий размер кода уменьшился. но разница за счет включения класса в код осталось прежней - порядка 8кбайт
Символьная таблица ELF файла прошивки, отсортированная по размеру:
Тут я не вполне понимаю. Например, самый большой символ в коде - это вектор прерывания CAN_USB размером аж 1.5к. Зачем он тут? Поддержку USB я вообще выключил... а кана у меня в программе нет. Или векторы прерываний включаются в код всегда?
Да и вообще если внимательно посмотреть - в списке всего тр-четыре раза всплывают функции моего класса, остальное все - системные вызовы. Значит сделать ничего нельзя?
А где такой же файл, но без экземпляра класса?
А где такой же файл, но без экземпляра класса?
продолжаем наблюдение
Заменил ARM-GCC тулчейн с древней версии 4.8.3(такая встроена в Ардуино ИДЕ для поддержки Дуе и более новой нет) на более свежую 9.2.1, как сам же писал вот тут
Итог
"Пустой скетч" всего 6720 байт
Мой код с классом - 13544 байт
Причина такого влияния на размер кода, как пишут - более продвинутая поддержка LTO в новых версиях тулчейна.
По сравнению с началом разница огромная - "выиграли" в размере уже более 7к. НО!
Разница между "пустым" скетчем и кодом с классом в результате всех усилий почти не изменилась! - было 8к, теперь чуть менее 7к
Поэтому вопрос из заголовка остается актуальным - как помочь линкеру удалить неиспользуемые методы?
скорее всего, компилятор, когда встречает обьявление класса, сразу неявно вставляет туда RTL поддержки для классов
скорее всего, компилятор, когда встречает обьявление класса, сразу неявно вставляет туда RTL поддержки для классов
ИМХО поддержка классов идет на уровне компилятора и RTL для этого в явном виде не существует
Беглый взгляд на два листинга показывает, что Ваш класс подтащил за собой библиотеку работы с памятью (malloc ...), библиотеку вычислений повышенной точности (__udivdiv3 ..) и, наверное, что то еще кроме самого себя.
как помочь линкеру удалить неиспользуемые методы?
Удалить их самому
Беглый взгляд на два листинга показывает, что Ваш класс подтащил за собой библиотеку работы с памятью (malloc ...),
=== да. это верно, она мне нужна
=== а вот вычислений у меня нет, надо будет разобраться. откуда оно... может какой-то из системных модулей тащит
Не помню, как в GCC, но в IAR можно было получить у линкера перечень перекрестных ссылок, из него ясно, что тащит за собой Ваш класс, и вроде даже где именно тащит, в какой функции.
Не помню, как в GCC, но в IAR можно было получить у линкера перечень перекрестных ссылок, из него ясно, что тащит за собой Ваш класс, и вроде даже где именно тащит, в какой функции.
Спасибо, тут это делает строчка
результат
после перерыва вновь вернулся к изысканиям насчет размера кода.
Напомню суть - разбираюсь, как в аддоне Кларка для СТМ избавится от включения в код неиспользуемых функций и процедур.
Например - из листинга в #38 видно, что самой большой секцией в коде является вектор __irq_usb_lp_can_rx0. Это тем более обидно, что в коде CAN и USB не используется, USB-загрузчик отключен (загрузка через St-link)
Похоже, что аддон Кларка тянет USBlib в код по умолчанию Все дефайны типа STM32_MCU_HAS_USB выключил - и все равно вижу в логе сборки, что usb-lib компилируется.
Вопрос - как ее убрать? Она точно влияет на код. Попробовал тупо заменить вектор __irq_usb_lp_can_rx0 на заглушку - код сразу уменьшился более чем на 1к
PS оптимизация с опцией -0s, LTO включено
Похоже, что аддон Кларка тянет USBlib в код по умолчанию
Вопрос - как ее убрать? Она точно влияет на код. Попробовал тупо заменить вектор __irq_usb_lp_can_rx0 на заглушку - код сразу уменьшился более чем на 1к
отвечаю сам себе.
Хоть я жутко ленивый, но в выходные выполнил тупую работу - методично заменил все функции и методы USBlib на пустые заглушки. Результат - размер прошивки не изменился.
Вывод1 - работа была лишней, компилятор с линкером отлично справляются с выкидыванием неиспользуемых функций из кода. А то, что их компиляция видна в логе - ничего не значит, они только компилируются. но в итоговый бинарник не попадают.
Однако у векторов прерывания поведение другое. Как уже писал выше, замена вектора __irq_usb_lp_can_rx0 на пустышку дала уменьшение размера кода на 1к. Тоже самое когда почистил вектора __irq_i2c и __irq_i2c_error(в точных названиях могу наврать, пишу по памяти) - выиграл суммарно еще 1к.
Вывод2 - оптимизатор не удаляет вектора прерываний из кода, да если они ни разу не вызываются в программе.
Хотелось бы услышать мнение старших, мои предположения верные?
Вывод2 - оптимизатор не удаляет вектора прерываний из кода, да если они ни разу не вызываются в программе.
Дак он и не может предсказать, будут ли они вызываться, поэтому оставляет все, на всякий случай. Прерывания на то и прерывания, что в явном виде ниоткуда не вызываются.
Спасибо.
не знаю, стоит ли возиться и добавлять в вектора прерываний директивы условной компиляции, чтобы их можно было "выключать", когда они не используются? Или это криво и костыльно?
Цель - ширше задействовать для проектов плату STM32F103C6, у которой флеш 32К вместо 64к/128к у блюпила