Ресурси щодо написання ефективного коду С для мікроконтролерів? [зачинено]


15

Тут потрібна серйозна допомога. Я люблю програмування. Останнім часом я читав купу книг (таких як K&R) та статей / форумів для мови C. Навіть спробував заглянути в код Linux (хоча, я загубився з чого почати, але зазирнути в невеликі бібліотеки допомогло?).

Я почав як Java-програміст, і на Яві це досить різано і сухо; Якщо програм стає занадто великим, наріжте його класами, а потім перейдіть до функцій. Такі правила, як код, читають код та додають коментарі. Використовуйте приховування інформації та методи OOP. Частина з яких досі стосується C.

Я кодую в C зараз і поки що я отримую програми для роботи так чи інакше. Дуже багато людей говорять про ефективність / ефективність, алгоритм / дизайн, оптимізацію та ремонтопридатність. Деякі люди підкреслюють один одного, ніж інший, але для непрофесійних інженерів програмного забезпечення ви часто чуєте щось на зразок: Linux-ядро Linux не просто прийме код.

Моє запитання таке: я планую написати код для 8-бітового мікроконтролера, не витрачаючи ніяких ресурсів . Знайте, що я родом з тла Java, тому речі вже не однакові ... ресурси / книги / посилання / поради будуть дуже вдячні. Продуктивність та розмір зараз мають значення. Ресурси / хитрощі щодо ефективного (в рамках найкращих практик) коду С для 8-бітових мікроконтролерів?

Крім того, inline assemblyвідіграє важливу роль, а також наближається до стандарту мікроконтролерів. Але чи є загальне правило ефективності, яке стосується всіх?

Наприклад: register unsigned int variable_name;переважніше charбудь-коли. Або використовувати, uint8_t якщо вам не потрібні великі цифри.

РЕДАКЦІЯ: Дякую вам за всі відповіді та пропозиції. Я ціную всі зусилля для обміну знаннями.


2
Це не часто трапляється на процесорі x86, але на мікроконтролері, якщо ви хочете переконатися, що ви отримуєте кожен останній спад продуктивності, ви, ймовірно, захочете скористатись збіркою замість цього.
Рей Міясака

3
@Rei: складені вручну складання рідко, якщо взагалі, використовують менше пам'яті і швидше, ніж сучасні компілятори C. Це марно витрачати час на кодування при складанні, коли це можна (як слід) зробити в C.
mattnz

1
@mattnz Сучасно, про що нового ти говориш? Чесно кажучи, я майже не десятиліття не писав код для мікроконтролера.
Рей Міясака

2
Один простий підказок: в мікроконтролерах все ще вірно, що "якщо це простіше, то швидше". На складних мікросхемах (ARM і вище) апаратне забезпечення робить стільки оптимізацій, що ви ніколи не дізнаєтесь, поки не протестуєте.
Хав'єр

1
@ReiMiyasaka при значному збільшенні витрат на обслуговування. Хороший компілятор C може створити майже той самий код, що і досвідчений програміст.

Відповіді:


33

У мене 20+ років вбудованих систем, переважно 8 і 16 мікросхем. Коротка відповідь на ваше запитання така ж, як і будь-яка інша розробка програмного забезпечення - не оптимізуйте, поки не знаєте, що потрібно, а потім не оптимізуйте, поки не дізнаєтесь, що потрібно для оптимізації. Напишіть код, щоб він був спочатку надійним, читабельним та доступним. Передчасна оптимізація - це стільки ж, якщо не більше, проблеми вбудованих систем

Коли ви програмуєте "не витрачаючи ніяких ресурсів", чи вважаєте ви свій час ресурсом? Якщо ні, то хто платить вам за ваш час, а якщо ніхто, чи маєте ви щось краще? Після вибору, який повинен зробити будь-який вбудований системний дизайнер, це вартість обладнання та вартість інженерного часу. Якщо ви будете перевозити 100 одиниць, використовуйте більший мікроавтобус на 100 000 одиниць, економія $ 1,00 за одиницю - це те саме, що 1 чоловічий рік розробки програмного забезпечення (ігнорування часу на ринок, можливі витрати тощо), на 1 мільйон одиниць, ви починаєте отримуючи рентабельність інвестицій за нав'язливість щодо використання ресурсів, але будьте обережні, оскільки багато вбудованих проектів ніколи не склали позначку в 1 мільйон, тому що вони мали намір продати 1 мільйон (Високі початкові інвестиції з низькою собівартістю виробництва), і вони занепали, перш ніж потрапити туди.

З цього приводу потрібно враховувати речі, про які слід пам'ятати (малі) вбудовані системи, оскільки вони перестануть працювати несподівано, а не просто повільно.

a) Стек - зазвичай у вас лише невеликий розмір стека і часто обмежений розмір кадру стека. Ви повинні знати про те, якою є можливість використання ваших стеків у будь-який час. Будьте попереджені, проблеми зі штабелем викликають деякі найбільш підступні дефекти.

б) Купа - знову ж таки, невеликі розміри купи, тому будьте обережні щодо необґрунтованого розподілу пам'яті. Фрагментація стає проблемою. З цими двома вам потрібно знати, що ви робите, коли ви закінчуєтесь - це не відбувається на великій системі через ОС, що надається підказками. тобто коли malloc повертає NULL, чи перевіряєте ви його і що ви робите. Кожна мальва потребує перевірки та обробника, роздуття коду ?. Як посібник - не використовуйте його, якщо є альтернатива. Більшість малих систем з цих причин не використовують динамічну пам'ять.

в) Обладнання перериває - Ви повинні знати, як з цим вирішуватись безпечно та своєчасно. Вам також потрібно знати, як зробити безпечний код повторного вступу. Наприклад, C-стандартні ліфти, як правило, не приймаються повторно, тому не повинні використовуватися всередині оброблювачів переривань.

г) складання - майже завжди передчасна оптимізація. Щонайменше, невелика кількість (накреслена) потрібна для досягнення чогось, що C просто не може зробити. Як вправу напишіть невеликий метод у складеній вручну складі (з нуля). Зробіть те саме в C. Виміряйте ефективність. Б'юсь об заклад, що C буде швидше, я знаю, що він буде більш читабельним, легкодоступним та розширюваним. Тепер для частини 2 вправи - напишіть корисну програму в збірці та C.
Як ще одну вправу, подивіться, яка частина ядра Linux є асемблером, а потім читайте, параграф нижче про ядро ​​Linux.

Варто знати, як це зробити, можливо, варто було б також володіти мовами для одного або двох загальних мікросхем.

д) "зареєструвати без підпису int змінну_назви", "зареєструвати" є, і завжди було натяком на компілятора, а не на інструкцію, ще на початку 70-х (40 років тому) це мало сенс. У 2012 році це марно натискання клавіш, оскільки компілятори такі розумні, а інструкції з мікросхем настільки складні.

Поверніться до вашого коментаря linux - проблема, яку ви тут маєте, полягає в тому, що ми говоримо не про один мільйон одиниць, а про 100-мільйонні, з вічним життям. Час та інженерні витрати, щоб зробити його максимально оптимальним, варті того часу. Хоча гарний приклад найкращої інженерної практики, для більшості розробників вбудованих систем було б комерційне самогубство таким же педантичним, як цього вимагає ядро ​​Linux.


4
mattnz: це одна з найкрасивіших відповідей на сайтах .stackexchange.
Ахмед Масуд

1
Я не можу покращити цю відповідь. Я можу лише додати, що введення коду асемблери рідко має сенс для продуктивності, але це може мати сенс для таких речей, як ткнинг мікросхем вводу / виводу або інші технічні трюки, які можуть бути непростими в C.
Майк Данлаве

@mattnz Дякую за добре поставлену відповідь. +1
AceofSpades

1
@MikeDunlavey ассемблер іноді потрібен для точних термінів. Тільки що закінчив відео з накладенням , який використовує біт стукати , щоб генерувати NTSC відео на штифтом I / O, вибір часу з точки зору - високої напруги для циклів 3clock, а потім в протягом 6 низького тоді ....
Мартін Бекетт

@Martin: Це має ідеальний сенс. Давно минуло, коли я зашифрував на цьому рівні.
Майк Данлаве

3

Ваше запитання ("не витрачаючи ресурси") занадто загальне, тому важко дати багато порад. Якщо взяти буквально, якщо ви не хочете витрачати ресурси, можливо, вам слід зробити крок назад і оцінити, чи потрібно взагалі щось робити, тобто чи можете ви вирішити проблему іншими способами.

Також корисна порада дуже залежить від ваших обмежень - яку систему ви будуєте та який процесор ви використовуєте? Це важка система реального часу? Скільки у вас пам’яті для коду та даних? Чи підтримує він в основному всі операції з С (особливо, множення та ділення) та для яких типів? Більш загально: прочитайте весь інформаційний аркуш і зрозумійте його.

Найважливіша порада: тримайте це просто.

Наприклад: забудьте про складні структури даних (хеші, дерева, можливо, навіть пов'язані списки) та використовуйте масиви фіксованого розміру. Використання більш складних структур даних гарантується лише після того, як ви вимірюванням довели, що масиви занадто повільні.

Крім того, не передумовте (те, до чого мають схильність Java / C # devs): пишіть простий процедурний код, не надто багатошарово. Абстракція коштує!

Будьте задоволені ідеєю використання глобальних змінних та goto [дуже корисно для очищення за відсутності винятків];)

Якщо вам доводиться стикатися з перебоями, читайте про рентранс. Написання рецензійного коду дуже нетривіально.


3

Я згоден з відповіддю mattnz - здебільшого. Я почав програмувати на 8085 понад 30 років тому, потім Z80, потім швидко перейшов на 8031. Після цього я перейшов до мікроконтролерів серії 68300, потім 80x86, XScale, MXL і (пізніше) 8-бітових фотографій, які я здогадка означає, що я прийшов повним колом. Для запису можу зазначити, що FAE у кількох великих виробників мікропроцесорів все ще використовують асемблер, хоча і об'єктно орієнтований для цілеспрямованого використання коду.

Те, що я не бачу у схваленій відповіді, - це обговорення цільового типу процесора та / або запропонована архітектура. Це 0.50 $ 8 гіркий з обмеженою пам’яттю? Це ядро ​​ARM9 з конвеєрним рухом та 8Meg спалаху? Копроцесор управління пам'яттю? Чи буде у нього ОС? Час (1) петля? Споживчий пристрій із загальним виробничим циклом 100000 одиниць? Стартова компанія з великими ідеями та мріями?

Хоча я згоден, що сучасні компілятори роблять велику роботу з оптимізації, я ніколи не працював над проектом протягом 30 років, де я не зупиняв налагоджувач і переглядав згенерований код складання, щоб побачити, що відбувається під капотом ( безумовно, кошмар, коли трубопроводи та оптимізація починають грати), тому знання складання важливі.

І я ніколи не мав генерального директора, віце-інженера, замовника, який не підштовхував мене спробувати забити галон в контейнер для кварту або заощадити. так? Що так важко?). Оптимізація пам'яті (код або дані) завжди буде враховуватися.

Моя думка, якщо ви будете розглядати проект з точки зору чистої програмування, ви отримаєте більш вузьке рішення сфери застосування. У Матнца це правильно - налаштуйте його на роботу, потім працюйте швидше, менше, краще, але вам потрібно витратити багато часу на вимоги та результати, перш ніж ви навіть задумаєтесь про кодування.


Привіт Джіо, будь ласка, уникай зайвого HTML у своїх публікаціях і використовуй натомість синтаксис Markdown . Для <br />вас може просто натисніть клавішу введення, і для абзаців просто залиште порожній рядок між ними. Також коли ви посилаєтесь на іншу відповідь, будь ласка, додайте посилання на неї. На даний момент може бути лише кілька відповідей, але їх може бути більше, розподілених на багатьох сторінках, і не буде дуже зрозуміло, який саме ви маєте на увазі. Перегляньте історію редагування, щоб побачити мої зміни.
янніс

@Gio Дякую за згадування інших важливих факторів. +1 :)
AceofSpades

+1 - приємне розширення моєї відповіді.
mattnz

1

Відповідь Мантца дуже добре визначає найбільш ключові моменти щодо того, як робити програмування "близько до апаратури". Це врешті-решт, для чого призначений C.

Однак я хотів би додати, що, хоча строгого ключового слова "Клас" не існує в С - це цілком прямо вперед подумати з точки зору об'єктно-орієнтованого програмування на С, навіть коли ти близько до апаратного забезпечення.

Ви можете розглянути цю відповідь: кращі практики OO для програм C, що пояснює цей пункт.

Ось декілька ресурсів, які допоможуть вам написати хороший об’єктно-орієнтований код у C.

а. Об'єктно-орієнтоване програмування в C
b. це хороше місце, де люди обмінюються ідеями
c. і ось повна книга

Ще один хороший ресурс, який я хотів би запропонувати вам:

Серія "Великий код запису" . Це двотомна книга. Перша книга охоплює дуже важливі аспекти машин на роботах нижчого рівня. Друга книга стосується "Мислення низького рівня - написання високого рівня"


1

У вас є кілька питань. спочатку ви хочете, щоб цей проект / код був портативним? Переносність коштує вашої продуктивності та розміру, чи може ваша платформа за вибором і завданням, яке ви реалізуєте, терпіти зайвий розмір і низьку продуктивність?

Так, абсолютно на 8-бітовій машині, що повертає неподписані символи замість ненаписаних вкладишів або шортів, є одним із способів поліпшити продуктивність та розмір. Так само на 16-бітній машині використовують непідписані шорти, а на 32-бітній машині без підписів. Ви можете легко зрозуміти, що якщо ви, наприклад, просто використовували неподписані вставки скрізь для переносимості через систему, яка переймає (ARM, наприклад, просуваючись до найменшої потужності, найменших ринків пристроїв), цей код є величезним рогом 8-бітний мікрофон. Звичайно, ви можете просто використовувати без підпису без int, short або char і дозволити компілятору вибрати оптимальний розмір.

Не просто вбудована збірка, а мова складання загалом. Вбудована збірка дуже не портативна і складніше писати, ніж просто викликати функцію asm. так, ви записуєте налаштування дзвінків і повертаєтесь, але ціною простішої розробки, кращого обслуговування та контролю. Правило все ще застосовується, запишіть його лише в ASM, якщо вам це дійсно потрібно, чи зробили ви роботу, щоб зробити висновок про те, що вихід компілятора - це ваша проблема в цій галузі та скільки виграшу в продуктивності ви можете отримати, зробивши це вручну. Потім поверніться до переносимості та обслуговування, як тільки ви почнете змішувати C і asm, і кожен раз, коли ви змішуєте C і asm, ви можете завдати шкоди своїй переносимості, і це може зробити проект менш ретельним, залежно від того, хто ще працює над ним або якщо це це продукт, який ви розробляєте зараз, і хтось інший повинен підтримувати дорогу. Після того, як ви зробите цей аналіз, ви автоматично знаєте, чи потрібно вам рухатися в рядку чи рухатися з прямою збіркою. У мене є 25+ років у цій галузі, пишу суміші C і Asm щодня, живу на / на апаратному / програмному рівні і, ну, ніколи не використовую вбудований ASM. Рідко варто докладати зусиль, занадто специфічний для компілятора, я пишу компілятор неспецифічний код, де це можливо (майже скрізь).

Ключовим моментом у вашому питанні є розбирання коду С. Дізнайтеся, що компілятор робить з вашим кодом, і з часом, якщо ви цього хочете, ви можете навчитися маніпулювати компілятором, щоб генерувати потрібний код, не прибігаючи до asm. З більшою кількістю часу ви можете навчитися маніпулювати компілятором для отримання ефективного коду для декількох цілей, що робить код більш портативним, не звертаючись до ASM. У вас не повинно виникнути проблем із тим, чому неподписаний char працює краще як повернення статусу від функції, ніж неподписаний на 8-бітовому мікро, так само неподписаний char стає дорожчим у 16 ​​та 32 бітових системах (деякі архітектури допомагають вам вихід, деякі не).

Деякі 8-бітові мікроконтролери, все? - дуже компіляторні, і жоден компілятор не створює хорошого коду. Немає достатнього попиту створити ринок компілятора для цих пристроїв, щоб створити чудовий компілятор для цих цілей, тому компілятори, які там є, приваблюють більше бізнес-програмістів, що не мають asm, а не тому, що компілятор краще, ніж рукописний ASM . рука і миші, потрапляючи в цей світ, змінюють цю модель, оскільки у вас є цілі, у яких є компілятори, які провели багато роботи над ними, компілятори, які виробляють досить хороший код і т. д. Для мікрофонів з такими процесорами, як ви, звичайно, все ще є випадок, коли вам доведеться перейти до asm, але це не так часто, набагато простіше просто сказати компілятору, що ви хочете зробити, ніж не використовувати його. Зауважте, що маніпулювання компілятором - це не якийсь потворний, нечитабельний код, насправді це навпаки, приємний чистий, простий код, можливо, переупорядкувавши кілька пунктів. Контролюючи розмір своїх функцій та кількість параметрів, така річ має величезну різницю у виведенні компілятора. Уникайте особливостей компілятора або мови ghee-whiz, KISS, будьте простою дурною, часто створює набагато кращий і швидший код.


Ви не вказуєте тип продукції, яку ви виробляєте, я вважаю, що це або дуже великий обсяг, низька маржа, або конкретний ринковий ринок з божевільною маржею. Це дуже важливо для прийняття рішення на рівні бізнесу, якщо ви повинні використовувати невеликий 8-бітовий мікро-і ручний асемблер, або більший мікро-і C. Насправді до вашого (видаленого?) Коментаря - я працюю з 8-бітовими мікросхемами, проте ми Почніть з більшого, ніж потрібно, мікро і перегляньте вниз лише тоді, коли і якщо вартість BOM стане проблемою) Час на ринок, вартість запуску та амортизовані витрати на розробку дозволяють нам дозволити додавати 10 або 20 центів до BOM.
mattnz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.