понеділок, 7 листопада 2016 р.

Таймери STM32 -- порівняння розмірів коду для CMSIS i HAL

В процесі написання серії постів про таймери склалася цікава ситуація. Ми маємо пари однотипних проектів, в одному із яких задача вирішується за допомогою 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


Захоплення вводу



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 -- зовсім окрема історія! Спеціально не досліджував, але маю певні підстави бути песимістичним. 
Якщо LTO недоступна, то вибір складніший. Але, якщо врахувати час на розробку, поки прошивка в мікроконтролер влазить, може і не вартує економити кілобайти flash-у... В кінці кінців, є компромісний варіант -- вручну реалізовувати лише ті частини програми, які особливо роздуваються або сповільнюються HAL-ом -- пишучи власний низькорівневий код чи редагуючи функції HAL.

Додатково

Безвідносно до HAL, ми отримали можливість порівняти, як ведуть себе різні режими оптимізації на кількох різних прикладах коду. Порівнюючи їх, бачимо наступне:
  • LTO не обов'язково дає найменший код -- для крихітних CMSIS варіантів розмір іноді зростав. Але, зазвичай, таки дає, тому її використання по замовчуванню є розумним рішенням.
  • LTO любить збільшувати використання RAM -- більше ніж в половині випадків. Але зростання незначне -- десятки байт. 
  • Без використання LTO, різниця у розмірі прошивки між O3 (оптимізувати за швидкодією) i Os (оптимізувати за розміром) -- доволі помітна, 20-30%. Якщо ж є  LTO -- часто складає лічені відсотки. 

Оптимізацію для контролерів часто уникають. Іноді це пов'язано із багами компіляторів, ("Помилка оптимізації GCC-AVR") однак, значно частіше -- із помилками в коді, зокрема -- бібліотечному (навіть в коді від ARM Holdings: "Помилка в ARM CMSIS"). Типовий приклад -- "вдало" забутий volatile, і код без оптимізації "чудово" працюватиме, а оптимізований -- ні.

Тому, багато готових бібліотек, доступних в Інтернеті, кажуть "компілювати без оптимізації".  Однак, як видно вище, виграш у розмірі прошивки буває дуже суттєвим. Не менш разючою може бути різниця у швидкодії коду. Тому, не слід відкидати її зразу! Навіть якщо частина коду не придатна для використання із оптимізацією, можна вимкнути її лише для нього.

На разі -- все,

Дякую за увагу!

Немає коментарів:

Дописати коментар