Як документувати та навчати інших "обчислюваним до невпізнання" обчислювально інтенсивним кодом?


11

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

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

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

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

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

Пов'язані:


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

1
І не просто кажіть їм, що робить код, але чому це швидше таким чином. При необхідності включіть посилання на алгоритми, або власні, такі як вікі, документи або ресурси, доступні в Інтернеті (просто пам’ятайте про гниття посилань у такому випадку, можливо, було б розумно скопіювати його у вашу власну систему документів із посиланням на оригінал .)
Мар'ян Венема

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

2
@sleske Іноді просто більше бінарного коду може сповільнити його.
Quant_dev

@quant_dev: Так, це може статися. Я просто думаю, що важливо, щоб код створювався та виконувався (в ідеалі) регулярно, щоб він був оновлений. Можливо, побудуйте його лише в режимі налагодження.
sleske

Відповіді:


10

Коротка відповідь

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

Повна відповідь

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

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

Пам'ятайте:

Перше правило оптимізації програми: не робіть цього.

Друге правило оптимізації програми (лише для експертів!): Не робіть цього ще ".

- Майкл А. Джексон

Для того, щоб знати, чи зараз потрібен час бенчмаркінгу та тестування.

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

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

Приклад

Наприклад, скажімо, що у вас функція швидкого перетворення Фур'є . Можливо, у вас є основна, алгоритмічна реалізація fft.cта тести в fft_tests.c.

Потім приходить Pentium, і ви вирішите застосувати версію з фіксованою точкою за fft_mmx.cдопомогою інструкцій MMX . Пізніше з'являється pentium 3, і ви вирішите додати версію, яка використовує Streaming SIMD Extensions в fft_sse.c.

Тепер ви хочете додати CUDA , тому ви додаєте fft_cuda.c, але виявите, що за допомогою тестового набору даних, який ви використовуєте роками, версія CUDA повільніше, ніж версія SSE! Ви робите деякий аналіз і в кінцевому підсумку додаєте набір даних, що в 100 разів більший, і ви отримуєте очікувану швидкість, але тепер ви знаєте, що час налаштування для використання версії CUDA є значним і що з невеликими наборами даних ви повинні використовувати алгоритм без встановлення вартості.

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

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

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


7

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

Поки ви не згадали, я припускаю Cтут.

Найпростіший спосіб в Cкоді - оптимізація (а також застосовується при спробі зробити речі портативними) - це зберігати

 
#ifdef OPTIMIZATION_XYZ_ENABLE 
   // your optimzied code here... 
#else  
   // your basic code here...

Коли ви включаєте #define OPTIMIZATION_XYZ_ENABLEпід час компіляції в Makefile, все працює відповідно.

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

основний код завжди виконується через функцію вказівника типу


   codec->computed_idct(blocks); 

Але покажчики функцій визначаються залежно від типу прикладу (наприклад, тут функція idct оптимізована для різної архітектури процесора.



if(OPTIMIZE_X86) {
  codec->computed_idct = compute_idct_x86; 
}
else if(OPTIMZE_ARM) {
  codec->computed_idct = compute_idct_ARM;
}
else {
  codec->computed_idct = compute_idct_C; 
}

ви повинні побачити код libjpeg та код libmpeg2 і може бути ffmpeg для таких методів.


6

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

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

  1. Використовуваний алгоритм повинен бути абсолютно зрозумілим.
  2. Мета кожного напряму реалізації повинна бути чіткою.
  3. Відхилення від очікуваних результатів повинні бути виявлені якнайшвидше.

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

Фактична реалізація, яка передається розробці, повинна бути задокументована таким чином, щоб усі тонкощі були виразними. Якщо ви придбаєте блокування в певному порядку, щоб уникнути тупикової ситуації, додайте коментар. Якщо ви перебираєте стовпчики замість стовпців матриці через проблеми з кеш-когерентністю, додайте коментар. Якщо ви робите що-небудь навіть трохи розумне, прокоментуйте це. Якщо ви можете гарантувати, що папір та код ніколи не будуть відокремлені (через систему VCS або подібну систему), ви можете повернутися до цього документу. Результат легко може бути коментарем понад 50%. Це нормально. Це підкаже, чому код робить те, що робить.

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

Моя найсердечніша рекомендація - не скупитися на жоден із кроків. Вам вони знадобляться пізніше;)


Дякую за всебічну відповідь. Я згоден з усіма вашими пунктами. Щодо автоматизованого тестування, я вважаю, що адекватно покрити числовий діапазон арифметики з фіксованою точкою та кодом SIMD важко, щось мене спалили двічі. Передумови, які були викладені лише в коментарях (без коду для посилення), не завжди були дотримані.
rwong

Тому я ще не прийняв вашу відповідь, тому що мені потрібно більше вказівок про те, що означає "коротка довідка" та які зусилля слід докласти для її створення. Для деяких галузей це частина основної сфери бізнесу, але в інших галузях варто враховувати витрати та брати юридично доступні ярлики.
rwong

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

2
На практиці бюлетень часто виглядає як перший проект наукової праці, без "пухнастих" частин (без змістовного вступу, без реферату, мінімальних висновків / дискусій та лише посилань, необхідних для його розуміння). Я розглядаю написання статті як звіт і невід'ємну частину розробки алгоритму та / або вибору алгоритму. Ви вирішили реалізувати цей алгоритм (скажімо, спектральний FFT). Що це саме? Чому ви вибрали саме цей за інших? Які його паралелізаційні характеристики? Зусилля повинні бути пропорційними роботі з відбору / розвитку.
drxzcl

5

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

Зауваження повинні містити цитати до технічних характеристик або технічного довідкового матеріалу.

Використовуйте загально галузеві термінології та назви алгоритмів, де це доречно - наприклад, «архітектура X генерує пастки процесора для нерівних зчитувань, тому пристрій Даффа заповнюється до наступної межі вирівнювання».

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

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


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