В процесі написання серії постів про таймери склалася цікава ситуація. Ми маємо пари однотипних проектів, в одному із яких задача вирішується за допомогою CMSIS, у іншому -- HAL. При чому, для "HAL-проектів", код ініціалізація створювався за допомогою STM32CubeMX, і є доволі громіздким, як ми не раз могли зауважити.
При чому, певна надія на оптимізатор компілятора залишається, але не надмірна -- оптимізації, які маніпулюють апаратними регістрами він просто "не посміє" робити.
Така ситуація дозволяє подивитися, скільки ж це задоволення -- зручне написання коду ініціалізації, коштує. При чому тут, на відміну від постів про далекомір, розмір C runtime i printf() не вноситиме величезну систематичну "похибку" -- він або присутній, або ні, в обох проектах одночасно.
Зауваження. Отримані вами значення для самостійно скомпільованих проектів можуть трохи відрізнятися, як через різні версії компілятора, так і через дрібні виправлення в проекті, з приводу яких немає бажання перекомпільовувати всі комбінації, лише щоб отримати уточнені на пару байт туди або сюди розміри для табличок нижче.
Якщо лінь, переходьте зразу до висновків.
Результати
Базовий відлік часу та PWM
Із першими постами "таймерної" серії ситуація трішки нетипова -- текстам "Таймери STM32 -- відлік часу/CMSIS" і "Таймери STM32 -- ШІМ/CMSIS" відповідає єдиний проект із симетричних постів "Таймери STM32 -- відлік часу/HAL" і "Таймери STM32 -- ШІМ/HAL". Видалося простішим, щоб мати можливість порівнювати, привести проекти до одного "функціоналу", додавши до CMSIS-коду із "Таймери STM32 -- відлік часу/CMSIS", який працює з TIM1 i TIM3, код з "Таймери STM32 -- ШІМ/CMSIS", котрий працює з TIM3.
CMSIS-проект | HAL-проект | |||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
O0 | 2352 | 1104 | 32 | 3488 | 57252 | 1088 | 216 | 58556 |
Os | 1704 | 1104 | 32 | 2840 | 31976 | 1088 | 216 | 33280 |
Os, LTO | 2104 | 1080 | 64 | 3248 | 4184 | 1088 | 240 | 5512 |
O3 | 1756 | 1104 | 32 | 2892 | 44532 | 1088 | 216 | 45836 |
O3, LTO | 2180 | 1080 | 64 | 3324 | 4604 | 1088 | 240 | 5932 |
Зовнішні та внутрішні тригери, біт OPM
Для простоти, ці проекти ("Таймери STM32 -- внутрішні тригери/CMSIS", "Таймери STM32 -- зовнішнє тактування/CMSIS", "Таймери STM32 -- автоматична зупинка/CMSIS" та "Таймери STM32 -- внутрішні тригери/HAL", "Таймери STM32 -- зовнішнє тактування/HAL", "Таймери STM32 -- автоматична зупинка/HAL") теж об'єднані в один. Гірше, що printf() тут таки використовується. Але, на різницю можна дивитися.CMSIS-проект | HAL-проект | |||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
O0 | 25840 | 2244 | 116 | 28200 | 80664 | 2228 | 356 | 83248 |
Os | 24608 | 2244 | 116 | 26968 | 54720 | 2228 | 356 | 57304 |
Os, LTO | 24400 | 2220 | 116 | 26736 | 26816 | 2228 | 356 | 29400 |
O3 | 24696 | 2244 | 116 | 27056 | 67424 | 2228 | 356 | 70008 |
O3, LTO | 24632 | 2220 | 116 | 26968 | 27152 | 2228 | 356 | 29736 |
Захоплення вводу
Проекти із постів "Таймери STM32 -- захоплення вводу/CMSIS" та "Таймери STM32 -- захоплення вводу/HAL". Використано printf().
CMSIS-проект | HAL-проект | |||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
O0 | 26336 | 2276 | 392 | 29004 | 81192 | 2260 | 448 | 83900 |
Os | 24912 | 2276 | 392 | 27580 | 54896 | 2260 | 440 | 57596 |
Os, LTO | 24496 | 2292 | 376 | 27164 | 26008 | 2260 | 448 | 28716 |
O3 | 25128 | 2276 | 392 | 27796 | 67776 | 2260 | 448 | 70484 |
O3, LTO | 24816 | 2292 | 376 | 27484 | 26440 | 2260 | 448 | 29148 |
Захоплення ШІМ
Проекти із постів "Таймери STM32 -- захоплення ШІМ/CMSIS" та "Таймери STM32 -- захоплення ШІМ/HAL". Використано printf().CMSIS-проект | HAL-проект | |||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
O0 | 25872 | 2244 | 140 | 28256 | 80680 | 2228 | 204 | 83112 |
Os | 24736 | 2244 | 140 | 27120 | 54688 | 2228 | 204 | 57120 |
Os, LTO | 24528 | 2220 | 140 | 26888 | 26152 | 2228 | 204 | 28584 |
O3 | 24784 | 2244 | 140 | 27168 | 67424 | 2228 | 204 | 69856 |
O3, LTO | 24688 | 2220 | 140 | 27048 | 26384 | 2228 | 204 | 28816 |
Запуск OPM кнопкою
З цим та наступним розділом ситуація зворотна, є два проекти на кожен із постів: "Таймери STM32 -- одноімпульсний режим/CMSIS" і "Таймери STM32 -- одноімпульсний режим/HAL".CMSIS-проект | HAL-проект | |||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
O0 | 2248 | 1104 | 32 | 3384 | 57076 | 1088 | 96 | 58260 |
Os | 1708 | 1104 | 32 | 2844 | 31864 | 1088 | 96 | 33048 |
Os, LTO | 2140 | 1080 | 56 | 3276 | 4108 | 1088 | 120 | 5316 |
O3 | 1724 | 1104 | 32 | 2860 | 44396 | 1088 | 96 | 45580 |
O3, LTO | 2196 | 1080 | 56 | 3332 | 4240 | 1088 | 120 | 5448 |
Запуск OPM іншим таймером
CMSIS-проект | HAL-проект | |||||||
---|---|---|---|---|---|---|---|---|
text | data | bss | Разом | text | data | bss | Разом | |
O0 | 2336 | 1104 | 32 | 3472 | 57228 | 1088 | 152 | 58468 |
Os | 1768 | 1104 | 32 | 2904 | 31992 | 1088 | 152 | 33232 |
Os, LTO | 2200 | 1080 | 56 | 3336 | 4320 | 1088 | 184 | 5592 |
O3 | 1788 | 1104 | 32 | 2924 | 44528 | 1088 | 152 | 45768 |
O3, LTO | 2260 | 1080 | 56 | 3396 | 4492 | 1088 | 184 | 5764 |
Висновки
Перше, що кидається в очі у всіх прикладах -- розміри прошивки для HAL без використання LTO просто жахливі! Його використання додає багато десятків кілобайт коду (50Кб+!). Якщо розглянути варіанти без printf(), то замість 2-3Кб для CMSIS, ми отримуємо 55-60Кб -- зростання в пару десятків раз! Правда, це "разове" збільшення розміру -- збільшення самої програми мало впливає на ці накладні витрати, але, все рівно -- виглядає марнотратно.З іншого боку, розмір потрібного RAM практично не змінюється. Якщо не забути додати data i bss, трішки зростає -- відсотків на 10. Це швидше добра новина, бо оперативна пам'ять -- значно більш критичний ресурс, ніж flash-пам'ять.
Однак, із використанням LTO, розмір проекту зростає всього лиш на пару кілобайт!
Так, вдвічі для проектів без printf(), з 2 до 4Кб, але з 24Кб до 26Кб для тих, що використовують printf().Тому, якщо є можливість використовувати LTO -- плата за абстракцію HAL видається невисокою:
- Для розміру прошивки -- цілком прийнятна. Лічені кілобайти коду в обмін на значно простіше використання, можливість візуального створення конфігурації і певну переносимість коду між різними моделями мікроконтролерів STM32.
- Зростання потреби у RAM таке, яким можна нехтувати у більшості практичних випадків. (Так, я читав "Историю одного байта").
- Увага! Швидкодія коду HAL -- зовсім окрема історія! Спеціально не досліджував, але маю певні підстави бути песимістичним.
Додатково
Безвідносно до HAL, ми отримали можливість порівняти, як ведуть себе різні режими оптимізації на кількох різних прикладах коду. Порівнюючи їх, бачимо наступне:- LTO не обов'язково дає найменший код -- для крихітних CMSIS варіантів розмір іноді зростав. Але, зазвичай, таки дає, тому її використання по замовчуванню є розумним рішенням.
- LTO любить збільшувати використання RAM -- більше ніж в половині випадків. Але зростання незначне -- десятки байт.
- Без використання LTO, різниця у розмірі прошивки між O3 (оптимізувати за швидкодією) i Os (оптимізувати за розміром) -- доволі помітна, 20-30%. Якщо ж є LTO -- часто складає лічені відсотки.
Оптимізацію для контролерів часто уникають. Іноді це пов'язано із багами компіляторів, ("Помилка оптимізації GCC-AVR") однак, значно частіше -- із помилками в коді, зокрема -- бібліотечному (навіть в коді від ARM Holdings: "Помилка в ARM CMSIS"). Типовий приклад -- "вдало" забутий volatile, і код без оптимізації "чудово" працюватиме, а оптимізований -- ні.
Тому, багато готових бібліотек, доступних в Інтернеті, кажуть "компілювати без оптимізації". Однак, як видно вище, виграш у розмірі прошивки буває дуже суттєвим. Не менш разючою може бути різниця у швидкодії коду. Тому, не слід відкидати її зразу! Навіть якщо частина коду не придатна для використання із оптимізацією, можна вимкнути її лише для нього.
На разі -- все,
Дякую за увагу!
Немає коментарів:
Дописати коментар