Тримайте оптимізації на місцях, робіть їх очевидними, добре їх документуйте та полегшуйте порівняння оптимізованих версій між собою та з неоптимізованою версією як щодо вихідного коду, так і продуктивності роботи.
Повна відповідь
Якщо такі оптимізації дійсно є такими важливими для вашого продукту, то вам потрібно знати не тільки, чому саме оптимізації були корисні раніше, але й надавати достатньо інформації, щоб допомогти розробникам дізнатися, чи будуть вони корисні в майбутньому.
В ідеалі вам потрібно включити тестування працездатності в процес збирання, щоб ви дізналися, коли нові технології скасовують старі оптимізації.
Пам'ятайте:
Перше правило оптимізації програми: не робіть цього.
Друге правило оптимізації програми (лише для експертів!): Не робіть цього ще ".
- Майкл А. Джексон
Для того, щоб знати, чи зараз потрібен час бенчмаркінгу та тестування.
Як ви вже згадували, найбільша проблема високооптимізованого коду полягає в тому, що важко підтримувати так, наскільки це можливо, вам потрібно тримати оптимізовані частини окремо від неоптимізованих частин. Незалежно від того, чи будете ви це робити через компіляцію часу компіляції, виклики віртуальної функції або щось середнє, не має значення. Що важливо, це те, що під час запуску тестів ви хочете мати змогу протестувати проти всіх версій, які вас зараз цікавлять.
Я схильний будувати систему таким чином, щоб основна неоптимізована версія виробничого коду завжди могла бути використана для розуміння намірів коду, а потім будувати різні оптимізовані модулі поряд з цим, що містять оптимізовану версію або версії, явно документуючи там, де оптимізована версія відрізняється від базової. Під час запуску тестів (блок та інтеграція), ви запускаєте їх у неоптимізованій версії та на всіх поточних оптимізованих модулях.
Приклад
Наприклад, скажімо, що у вас функція швидкого перетворення Фур'є . Можливо, у вас є основна, алгоритмічна реалізація 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, коли базовий клас, який реалізує неоптимізований алгоритм, а похідні класи реалізують різні оптимізації.
Важливо - зберігати ті самі речі, які є однаковими , щоб відмінності були очевидними .