Відповідь @ vicatcu досить вичерпна. Ще одне, що потрібно відзначити, - це те, що при доступі до вводу-виводу процесор може працювати в станах очікування (зупинені цикли процесора), включаючи пам'ять програми та даних.
Наприклад, ми використовуємо TI F28335 DSP; деякі області оперативної пам’яті є станом 0-очікування для пам'яті програми та даних, тому при виконанні коду в оперативній пам’яті він працює з 1 циклом за інструкцією (за винятком тих інструкцій, які займають більше 1 циклу). Коли ви виконуєте код з пам'яті FLASH (вбудований EEPROM, більш-менш), він не може працювати на повному 150 МГц і в кілька разів повільніше.
Що стосується високошвидкісного коду переривання, ви повинні вивчити ряд речей.
По-перше, ознайомтеся зі своїм компілятором. Якщо компілятор робить хорошу роботу, це не повинно бути набагато повільніше, ніж ручне складання для більшості речей. (де "що набагато повільніше": коефіцієнт 2 мій буде нормальним; коефіцієнт 10 буде неприйнятним) Вам потрібно навчитися (і коли) використовувати прапори оптимізації компілятора, і раз у раз ви повинні шукати на виході компілятора, щоб побачити, як це робиться.
Деякі інші речі, які можна зробити компілятором для прискорення коду:
використовувати вбудовані функції (не пам'ятаю, чи підтримує це C або це лише C ++ - ism), як для невеликих функцій, так і для функцій, які виконуються лише один чи два рази. Недоліком вбудованих функцій важко налагодити, особливо якщо включена оптимізація компілятора. Але вони заощаджують вам непотрібні послідовності виклику / повернення, особливо якщо абстракція "функції" призначена для концептуальної розробки, а не для реалізації коду.
Перегляньте посібник Вашого компілятора, щоб побачити, чи має він внутрішні функції - це вбудовані функції, вбудовані залежно від компілятора, які відображаються безпосередньо в інструкції зі збирання процесора; деякі процесори мають інструкції по збірці, які роблять корисні речі, такі як min / max / біт назад, і ви можете заощадити час.
Якщо ви робите числові обчислення, переконайтеся, що ви не викликаєте зайвих функцій математичної бібліотеки. У нас був один випадок, коли код був чимось на зразок y = (y+1) % 4
лічильника, який мав період 4, очікуючи, що компілятор реалізує модуль 4 як біт-AND. Натомість вона називається математичною бібліотекою. Тож ми замінили y = (y+1) & 3
робити те, що хотіли.
Ознайомтеся зі сторінкою хак-битів . Я гарантую, що ви будете користуватися принаймні одним із них часто.
Ви також повинні використовувати таймерські периферійні пристрої процесора для вимірювання часу виконання коду - у більшості з них є таймер / лічильник, який можна встановити для роботи на тактовій частоті процесора. Зробіть копію лічильника на початку та в кінці вашого критичного коду, і ви зможете побачити, як триває це. Якщо ви не можете цього зробити, іншою альтернативою є опустити вихідний штифт на початку коду і підняти його в кінці, і подивитися на цей вихід на осцилограмі, щоб вчасно виконати виконання. У кожному підході є компроміси: внутрішній таймер / лічильник є більш гнучким (ви можете витратити декілька речей), але складніше вивести інформацію, тоді як встановлення / очищення вихідного штифта відразу видно на області застосування, і ви можете фіксувати статистику, але важко відрізнити кілька подій.
Нарешті, є дуже важливий навик, який має досвід - як загальний, так і конкретні комбінації процесор / компілятор: знати, коли і коли не оптимізувати . Взагалі відповідь не оптимізувати. Цитата Дональда Кнута часто публікується на StackOverflow (зазвичай це лише остання частина):
Слід забути про невелику ефективність, скажімо, про 97% часу: передчасна оптимізація - корінь усього зла
Але ви потрапили в ситуацію, коли ви знаєте, що вам потрібно зробити якусь оптимізацію, тож саме час кусати кулю і оптимізувати (або отримати швидший процесор, або те й інше). Ви НЕ писати всю ISR в зборі. Це майже гарантована катастрофа - якщо ви це зробите, протягом місяців або навіть тижнів ви забудете частину того, що ви зробили і чому, і код, ймовірно, буде дуже крихким і важко змінити. Однак, ймовірно, є частини вашого коду, які є хорошими кандидатами на збірку.
Ознаки того, що частини вашого коду добре підходять для кодування збірки:
- функції, які добре містять чітко визначені невеликі процедури, навряд чи зміниться
- функції, які можуть використовувати конкретні інструкції по збірці (min / max / shift shift / тощо)
- функції, які дзвонять багато разів (отримує вам множник: якщо ви зберігаєте 0,5usec на кожному дзвінку, і він викликається 10 разів, це заощаджує вам 5 Usec, що важливо у вашому випадку)
Дізнайтеся про функції вашого компілятора, що викликає конвенції (наприклад, де він розміщує аргументи в регістри та які регістри зберігає / відновлює), щоб ви могли писати підпрограми складання на C-дзвінки.
У моєму теперішньому проекті у нас є досить велика база коду з критичним кодом, який повинен працювати в 10 кГц переривання (100usec - звук знайомий?), І не так багато функцій, які записані в зборі. Це такі речі, як обчислення CRC, черги програмного забезпечення, компенсація посилення / компенсації ADC.
Удачі!