Чи справді вдала практика відключити оптимізацію під час фаз розробки та налагодження?


15

Я читав Програмування 16-бітових мікроконтролерів PIC в C , і в книзі є таке твердження:

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

Зізнаюся, я трохи розгубився. Я не розумів, чи автор сказав це через період оцінювання С30 чи це справді хороша практика.

Мені хотілося б знати, чи ви фактично використовуєте цю практику, і чому?

Відповіді:


16

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

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

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

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


5
Контрпунктом цього аргументу є те, що оптимізатор, швидше за все, зробить речі меншими та / або швидшими, і якщо у вас є код із обмеженням часу або розміру, ви можете щось порушити, відключивши оптимізацію, і витратити свій час на налагодження проблеми, яка не робить ' t насправді існує. Звичайно, налагоджувач може також зробити ваш код повільнішим та більшим.
Кевін Вермер

Де я можу дізнатися більше про це?
Даніель Грілло

Я не працював з компілятором C30, але для компілятора C18 була примітка / посібник до програми для компілятора, яка висвітлювала, які оптимізації він підтримує.
Марк

@ O Engenheiro: Перевірте документи компілятора, які оптимізації він підтримує. Оптимізація сильно відрізняється залежно від компілятора, бібліотек та цільової архітектури.
Майкл Коне

Знову ж таки, не для компілятора C30, але gcc публікує LONG-список різних оптимізацій, які можна застосувати. Ви також можете скористатися цим списком, щоб отримати тонкозернисту оптимізацію, якщо у вас є певна структура управління, яку ви хочете зберегти недоторканою. Список тут: gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Кевін Вермер

7

Я надіслав це запитання Джеку Ганслу, і він мені відповів:

Даніель,

Я віддаю перевагу налагодження з використанням будь-яких оптимізацій, які будуть у звільненому коді. NASA каже: "тестуй те, що летиш, літай те, що тестуєш". Іншими словами, не робіть тестування і не змінюйте код!

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

Все найкраще, Джеку


Я думаю, що у цій відповіді між двома параграфами є нестабільне відмінність. Тестування стосується процедури, яка повинна продемонструвати, що м'які, тверді та / або тверді вироби працюють належним чином. Налагодження - це процес, в якому код переходить через інструкцію за інструкцією, щоб зрозуміти, чому він не працює [поки].
Кевін Вермер

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

@reemrevnivek, коли ви налагоджуєте, ви теж не тестуєте?
Даніель Грілло

@O Engenheiro - Ні. Налагоджую лише тест, якщо тест не вдасться.
Кевін Вермер

6

Залежить, і це, як правило, стосується всіх інструментів, а не лише C30.

Оптимізації часто видаляють та / або реструктурують код різними способами. Ваша операція перемикання може повторно доповнити конструкцію if / else або в деяких випадках може бути видалена всі разом. y = x * 16 може бути замінено рядом лівих зрушень і т. д., хоча цей останній тип оптимізації зазвичай все-таки можна пройти через його, головним чином, реструктуризацію оператора управління, який отримує ya.

Це може унеможливити перехід налагоджувача через ваш код C, оскільки структури, визначені вами C, більше не існують, їх замінив або переупорядкував компілятор на те, на що, на думку компілятора, буде швидше або використовуватиме менше місця. Це також може зробити неможливим встановлення точок перерви з переліку С, оскільки інструкція, на яку ви перебиваєтесь, більше не може існувати. Наприклад, ви можете спробувати встановити точку зламу всередині оператора if, але компілятор, можливо, видалив це, якщо. Ви можете спробувати встановити точку розриву протягом певного часу або для циклу, але компілятор вирішив розкрутити цю петлю, щоб вона більше не існувала.

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

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

Інший gotcha може бути примірником коду до UC, можливо, вам знадобляться оптимізації щільності коду, щоб просто вписати свій код у чіп. Це одна з причин, чому зазвичай хороша ідея починати з найбільшої ємності ПЗУ в сім’ї і вибирати лише менший для виготовлення, після того як ваш код буде заблокований.


5

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

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


1
Але одноразовий крок коду може бути майже неможливим із включеними оптимізаціями. Налагоджуйте вимкнено оптимізацію та запустіть свої тести на пристрої з кодом випуску.
Rocketmagnet

3

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

Типовий режим відмов - коли посилення оптимізації призводить до появи раніше небачених помилок через те, що ви не оголосили змінні як мінливі там, де це необхідно - це важливо, щоб сказати компілятору, які речі не слід «оптимізувати».


2

Використовуйте будь-яку форму, яку ви збираєтеся випустити, налагоджувачі та компіляція для налагодження приховують багато (МНОГО) помилок, які ви не бачите, поки не компілюєте для випуску. На той час набагато складніше знайти ці помилки, проти налагодження, як ви йдете. 20 років тому, і я ніколи не використовував gdb або подібний налагоджувач, не потрібно спостерігати за змінними чи одним кроком. Сотні до тисяч рядків на день. Так що можливо, не приводьте думати інакше.

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

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


1
+1 просто для детальної вашої відповіді: часто компілюючи в режимі "налагодження", буде прокладено стек / купа навколо змінних з не виділеним простором, щоб зменшити невеликі помилки списання в кінці та форматування рядків. Ви частіше будете отримувати хороший збій роботи, якщо компілюєте у випуску.
Morten Jensen

2

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

Я вважаю, що gdb працює набагато краще з кодом -O0, і це набагато простіше дотримуватися, якщо вам доведеться вступити в збірку. Перемикання між -O0 та -O також дозволяє вам побачити, що компілятор робить з вашим кодом. Часом це досить цікава освіта, а також може виявити помилки компілятора ... ті неприємні речі, які змушують вас витягати волосся, намагаючись з’ясувати, що не так у вашому коді!

Якщо мені справді потрібно, я почну додавати в -fdata-розділи та -fcode-розділи з -gc-секціями, які дозволяють лінкеру видалити цілі функції та сегменти даних, які насправді не використовуються. Є багато дрібниць, з якими можна подумати, щоб спробувати зменшити речі далі чи зробити речі швидшими, але за великим рахунком це єдині хитрощі, які я в кінцевому підсумку використовую, і все, що має бути меншим чи швидшим, я передам -збирати.


2

Так, відключення оптимізації під час налагодження вже давно є найкращою практикою з трьох причин:

  • (а) якщо ви збираєтесь одномоментну програму за допомогою налагоджувача високого рівня, це трохи менш заплутано.
  • (a) (застаріле), якщо ви збираєтесь одноетапно налагоджувати програму за допомогою налагоджувача на мові асемблера, це набагато менш заплутано. (Але чому ви б це турбували, коли ви могли використовувати налагоджувач високого рівня?)
  • (b) (давно застарілий) ви, ймовірно, збираєтеся запустити цей конкретний виконуваний файл лише один раз, потім внесіть деякі зміни та перекомпілюйте. Марно витрачати час на очікування додаткових 10 хвилин, поки компілятор "оптимізує" саме цей виконуваний файл, коли це дозволить заощадити менше 10 хвилин виконання. (Це вже не актуально для сучасних ПК, які можуть скласти типовий виконуваний мікроконтролер, з повною оптимізацією, менше ніж за 2 секунди).

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


Однокроковий перехід через збірний код може бути дуже корисним для діагностики випадків, коли вихідний код насправді вказує щось інше, ніж те, що він виглядає (наприклад, "longvar1 & = ~ 0x40000000; longvar2 & = ~ 0x80000000;") або де компілятор генерує помилковий код . Я виявив деякі проблеми, використовуючи налагоджувачі машинного коду, які я дійсно не думаю, що я міг би відшукати будь-який інший спосіб.
supercat

2

Просте: оптимізація є трудомісткою і може бути марною, якщо вам доведеться змінити цей фрагмент коду пізніше в процесі розробки. Тож вони цілком можуть бути марною тратою часу та грошей.
Однак вони корисні для готових модулів; частини коду, які, ймовірно, більше не потребуватимуть змін.


2

це, безумовно, має сенс у випадку зривів ... оскільки компілятор може видалити безліч операторів, які насправді не впливають на пам'ять.

розглянути щось на кшталт:

int i =0;

for (int j=0; j < 10; j++)
{
 i+=j;
}
return 0;

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

for (int j=delay; j != 0; j--)
{
    asm( " nop " );
    asm( " nop " );
}
return 0;

1

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

Особисто я вважаю, що налагоджувач PIC викликає більше проблем, ніж допомагає мені виправити.
Я просто використовую printf () в USART для налагодження моїх програм, написаних на C18.


1

Більшість аргументів проти того, щоб оптимізувати процес компіляції, зводиться до:

  1. проблеми з налагодженням (підключення JTAG, точки перерви тощо)
  2. неправильні терміни програмного забезпечення
  3. sh * t перестає працювати правильно

ІМХО перші два легітимні, треті не дуже. Це часто означає, що у вас є поганий код або ви покладаєтесь на небезпечну експлуатацію мови / імплементації, або, можливо, автор є просто шанувальником хорошої поведінки дядька не визначеного.

У блозі Embedded in Academia є щось інше, щоб сказати про невизначену поведінку, і це повідомлення про те, як компілятори це використовують: http://blog.regehr.org/archives/761


Іншою можливою причиною є те, що компілятор може бути прикро повільним, коли ввімкнено оптимізацію.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.