Чи дійсно орієнтація на об'єкти впливає на продуктивність алгоритму?


14

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

OO дуже корисно швидко та легко в алгоритмах кодування. Але чи може цей OOP бути недоліком для програмного забезпечення на основі продуктивності, тобто як швидко виконується програма?

Наприклад, зберігання графічних вузлів у структурі даних здається в першу чергу "простим", але якщо об'єкти Node містять багато атрибутів і методів, чи може це призвести до повільного алгоритму?

Іншими словами, чи може багато посилань між багатьма різними об'єктами або, використовуючи багато методів з багатьох класів, призвести до "важкої" реалізації?


1
Досить дивне запитання. Я можу зрозуміти, як OOP допомагає на рівні архітектури. Але рівень реалізації алгоритмів, як правило, будується на абстракціях, які дуже чужі для всього, на чому виступає OOP. Тож, швидше за все, ефективність не є найбільшою проблемою для вашої реалізації алгоритмів OOP. Що стосується продуктивності, то для OOP найбільший вузький вузол зазвичай пов'язаний з віртуальними дзвінками.
SK-логіка

@ SK-логіка> орієнтація на об'єкт, як правило, маніпулює вічністю вказівником, що передбачає більш важливе навантаження на стороні розподілу пам'яті, а не локалізовані дані, як правило, не знаходяться в кеш-пам'яті процесора і, нарешті, не менш важливо, означають багато непрямих розгалуження (віртуальні функції), що смертельно небезпечно для конвеєра процесора. OO - це гарна річ, але вона, безумовно, може мати вартість продуктивності в деяких випадках.
deadalnix

Якщо у вузлів вашого графіка є сто атрибутів, вам потрібно буде їх зберігати незалежно від парадигми, яка використовується для фактичної реалізації, і я не бачу, як жодна парадигма має перевагу в цьому взагалі. @deadalnix: Можливо, постійні фактори можуть бути гіршими через посилення певних оптимізацій. Але зауважте, що я говорю важче , не неможливо - наприклад, PyPy може розблокувати об'єкти в тісних петлях, а JVM відтоді створює виклики віртуальних функцій.

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

1
+1 Для того, щоб пов'язувати орієнтацію об'єкта з алгоритмами, те, що сьогодні не помічено, як у галузі програмного забезпечення, так і в академії ...
umlcat

Відповіді:


16

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

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


2
Основна причина цього полягає в тому, що лише C ++ вирішив використовувати шаблони виразів, щоб зробити версію OO максимально ефективною.
DeadMG

4
Подивіться на сучасні бібліотеки C ++ (STL, Boost) - вони теж зовсім не OOP. І не тільки через продуктивність. Алгоритми зазвичай не можуть бути добре представлені у стилі ООП. Такі речі, як загальне програмування, набагато краще підходять для алгоритмів низького рівня.
SK-логіка

3
Що-що-що? Я думаю, я родом з іншої планети, ніж Quant_dev та SK-логіка. Ні, інший Всесвіт. З різними законами фізики і все.
Майк Накіс

5
@MikeNakis: різниця в точці зору полягає в (1), чи може певний обчислювальний код отримати користь з точки зору людської читабельності від OOP взагалі (яких чисельних рецептів немає); (2) чи узгоджується дизайн класу OOP з оптимальною структурою даних та алгоритмом (див. Мою відповідь); та (3) чи забезпечить кожен рівень опосередкування достатньою «величиною» (з точки зору виконаної роботи на виклик функції чи концептуальної ясності на шар), виправдовує накладні витрати (за рахунок непрямості, виклику функції, шарів або копіювання даних). (4) Нарешті, складність компілятора / JIT / оптимізатора є обмежуючим фактором.
rwong

2
@MikeNakis, що ти маєш на увазі? Як ви вважаєте, STL - це бібліотека OOP? Узагальнене програмування так чи інакше не підходить для OOP. І зайве згадувати, що ООП - це занадто вузька рамка, яка підходить лише для небагатьох практичних завдань, чужа для будь-чого іншого.
SK-логіка

9

Що визначає продуктивність?

Основи: структури даних, алгоритми, архітектура комп'ютера, апаратне забезпечення. Плюс накладні витрати.

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

Однак програма, яка спочатку розроблена лише з приводу проблем OOP, не стосуючись основ, може бути спочатку неоптимальною. Суб-оптимальність іноді можна усунути рефакторингом; іноді це не так - вимагати повного переписування.

Caveat: чи важлива робота в бізнес-програмному забезпеченні?

Так, але час на ринок (TTM) важливіший, на порядки. Програмне забезпечення для бізнесу робить акцент на пристосованості коду до складних бізнес-правил. Вимірювання ефективності слід проводити протягом життєвого циклу розвитку. (Див. Розділ: що означає оптимальна продуктивність? ) Необхідно вносити лише товарні вдосконалення та поступово впроваджувати їх у наступних версіях.

Що означає оптимальна продуктивність?

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

Іноді ця швидша версія вперше бачиться на іншій мові чи парадигмі. Це слід сприймати як натяк на покращення, а не на думку про неповноцінність деяких інших мов чи парадигм.

Чому ми робимо OOP, якщо це може заважати нашому пошуку оптимальної продуктивності?

OOP запроваджує накладні витрати (у просторі та виконанні), натомість для поліпшення "працездатності" і, отже, ділової цінності коду. Це зменшує витрати на подальший розвиток та оптимізацію. Дивіться @MikeNakis .

Які частини ООП можуть спонукати спочатку неоптимальний дизайн?

Частини ООП, які (i) заохочують простоту / інтуїтивність, (ii) використання методів розмовного розмови замість фундаментальних, (iii) відмовляє від декількох спеціалізованих реалізацій одного призначення.

  • KISS
  • ЯГНІ
  • СУХИЙ
  • Дизайн об'єкта (наприклад, з картками CRC), не даючи рівних думок фундаментам)

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

Які поширені заходи щодо зменшення накладних витрат OOP?

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

Деякі мови підтримують вбудований код, який може відновити деяку продуктивність виконання.

Як ми могли прийняти ООП без шкоди для виконання?

Вивчайте та застосовуйте як ООП, так і основи.

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

Чи існують типи коду, які не отримають користі від OOP?

(Розширено з обговорення між [@quant_dev], [@ SK-логікою] та [@MikeNakis])

  1. Числові рецепти, що походять з математики.
    • Математичні рівняння та самі перетворення можна розуміти як об’єкти.
    • Для створення ефективного виконуваного коду необхідні дуже складні методи перетворення коду. Наївна ("біла дошка") реалізація матиме безглузді результати.
    • Однак сьогоднішні компілятори мейнстріму не можуть цього зробити.
    • Спеціалізоване програмне забезпечення (MATLAB та Mathematica тощо) мають як JIT, так і символічні розв'язувачі, здатні генерувати ефективний код для деяких підзадач. Ці спеціалізовані вирішувачі можна розглядати як компілятори спеціального призначення (посередники між кодом, прочитаним людиною, та кодом, що виконується машиною), який сам отримає користь від проекту OOP.
    • Кожна підзадача потребує власного "компілятора" та "перетворення коду". Тому це дуже активна відкрита дослідницька область, щороку з’являються нові результати.
    • Оскільки дослідження займають тривалий час, розробникам програмного забезпечення доводиться проводити оптимізацію на папері та переписувати оптимізований код у програмне забезпечення. Переписаний код може справді не зрозуміти.
  2. Код дуже низького рівня.
      *

8

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

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


1
Оскільки компілятори є неоптимальними (або правила мови програмування забороняють користуватися певними припущеннями або оптимізаціями), насправді накладні витрати неможливо видалити. Крім того, певні оптимізації, наприклад, векторизація, мають вимоги до організації даних (наприклад, структура масивів замість масивів структур), які OOP можуть або покращувати, або перешкоджати. (Я нещодавно щойно працював над завданням std :: vector optimization.)
rwong

5

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

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

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

Один із способів цього проявляється - кількість разів в секунду , що виконується нове , яке, як передбачається, має O (1), але може виконувати сотні та тисячі інструкцій (включаючи відповідне час видалення або GC). Це можна усунути, зберігаючи використовувані об'єкти, але це робить код менш "чистим".

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

Можливо, продуктивність даного додатка просто чудова як написана.

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


3

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

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

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


1
+1: різниця між спагетті та об'єктно-орієнтованим кодом (або кодом, написаним у чітко визначеній парадигмі): кожна переписана версія хорошого коду приносить нове розуміння проблеми. Кожна версія перероблених спагетті ніколи не приносить розуміння.
rwong

@rwong не можна було пояснити краще ;-)
umlcat

3

Але чи може цей OOP бути недоліком для програмного забезпечення на основі продуктивності, тобто як швидко виконується програма?

Часто так !!! АЛЕ ...

Іншими словами, чи може багато посилань між багатьма різними об'єктами або, використовуючи багато методів з багатьох класів, призвести до "важкої" реалізації?

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

В інших мовах, таких як Java, є деякий наклад на об'єкт (часто він досить малий у багатьох випадках, але астрономічний у деяких рідкісних випадках із дійсно маленькими об'єктами). Наприклад, Integerвін значно менш ефективний, ніж int(займає 16 байт на відміну від 4 на 64-розрядному). Але це не просто кричущі відходи або щось подібне. В обмін на те, Java пропонує такі речі, як відображення кожного визначеного користувачем типу рівномірно, а також можливість змінити будь-яку функцію, не позначену як final.

Але давайте візьмемо найкращий сценарій: оптимізуючий компілятор C ++, який може оптимізувати інтерфейси об'єктів до нуля накладних витрат. Навіть тоді OOP часто погіршує продуктивність і не дасть їй досягти піку. Це може звучати як повний парадокс: як це могло бути? Проблема полягає в:

Дизайн інтерфейсу та інкапсуляція

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

Візьмемо цей приклад:

class Particle
{
public:
    ...

private:
    double birth;                // 8 bytes
    float x;                     // 4 bytes
    float y;                     // 4 bytes
    float z;                     // 4 bytes
    /*padding*/                  // 4 bytes of padding
};
Particle particles[1000000];     // 1mil particles (~24 megs)

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

Вже зараз ми можемо побачити яскраві 4-байтні накладки, необхідні для birthправильного вирівнювання елемента, коли частинки безперервно агрегуються. Вже ~ 16,7% пам'яті витрачається на мертвий простір, який використовується для вирівнювання.

Це може здатися суперечливим, оскільки у нас сьогодні є гігабайти DRAM. Але навіть у самих звіриних машин у нас часто є лише 8 мегабайт, якщо мова йде про найповільніший і найбільший регіон кеш-процесора (L3). Чим менше ми можемо поміститися там, тим більше ми заплатимо за це за багаторазовий доступ до DRAM, і чим повільніше вийдемо. Раптом втрата 16,7% пам’яті вже не здається тривіальною угодою.

Ми можемо легко усунути цю накладну без будь-якого впливу на вирівнювання поля:

class Particle
{
public:
    ...

private:
    float x;                     // 4 bytes
    float y;                     // 4 bytes
    float z;                     // 4 bytes
};
Particle particles[1000000];     // 1mil particles (~12 megs)
double particle_birth[1000000];  // 1mil particle births (~8 bytes)

Тепер ми зменшили пам’ять з 24 мег до 20 мег. За допомогою послідовного шаблону доступу машина тепер споживає ці дані трохи швидше.

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

Як наслідок, фактично важливі дані щодо продуктивності - це не 20 мегабайт, а фактично суміжний блок на 12 Мбайт. Фактична гаряча пам’ять, до якої ми часто звертаємось, скоротилася до половини свого розміру! Очікуйте значних прискорень роботи над нашим оригінальним, 24-мегабайтним рішенням (не потрібно вимірювати - вже зроблено подібний матеріал тисячу разів, але почувайтеся вільно, якщо сумніваєтесь).

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

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

Щоб продовжити це, скажімо, що оскільки ми просто переміщуємо частинки навколо, ми можемо отримати доступ до їх полів x / y / z у трьох окремих петлях. У цьому випадку ми можемо скористатись SIMD-стилями SIMD-стилів з регістрами AVX, які можуть векторизувати 8 операцій SPFP паралельно. Але для цього ми повинні використовувати це представлення:

float particle_x[1000000];       // 1mil particle X positions (~4 megs)
float particle_y[1000000];       // 1mil particle Y positions (~4 megs)
float particle_z[1000000];       // 1mil particle Z positions (~4 megs)
double particle_birth[1000000];  // 1mil particle births (~8 bytes)

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

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

Рішення

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

Але ми можемо прийняти остаточне подання, на якому ми опинилися і все ще моделюємо об'єктно-орієнтований інтерфейс:

// Represents a collection of particles.
class ParticleSystem
{
public:
    ...

private:
    double particle_birth[1000000];  // 1mil particle births (~8 bytes)
    float particle_x[1000000];       // 1mil particle X positions (~4 megs)
    float particle_y[1000000];       // 1mil particle Y positions (~4 megs)
    float particle_z[1000000];       // 1mil particle Z positions (~4 megs)
};

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

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

Тож це рішення у справжньо важливих для продуктивності областях, що справляються з великим навантаженням, і для всіх видів мов програмування (ця методика виграє на C, C ++, Python, Java, JavaScript, Lua, Swift тощо). І це не може бути легко позначено як "передчасна оптимізація", оскільки це стосується дизайну інтерфейсу та архітектури . Ми не можемо записати базу коду, що моделює одну частинку як об'єкт із завантаженою залежністю клієнта до aParticle'sпублічний інтерфейс, а згодом передумайте. Я багато чого зробив, коли закликали оптимізувати застарілі бази даних, і це може призвести до того, що потрібно багато місяців ретельно переписати десятки тисяч рядків коду для використання об'ємного дизайну. Це в ідеалі впливає на те, як ми проектуємо речі наперед, за умови, що ми можемо передбачити велике навантаження.

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


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

2

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

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

На алгоритмічному рівні часто розмірковують про більшу картину та обмеження чи зв’язки між значеннями, що призводять до посилення Big O. Прикладом може бути те, що в думці OOP немає нічого, що призвело б до перетворення "суми безперервного кола цілих чисел" з циклу в цикл(max + min) * n/2

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

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

Звичайно, як сказано в більшості відповідей, для більшості розробок для більшості алгоритмів ООП не має значення для продуктивності. Поки ви не "передчасно песимізуєте" і додаєте безліч не локальних дзвінків, thisвказівник ні тут, ні там.


0

Пов’язані, а часто і не помічені.

Це непроста відповідь, це залежить від того, що ви хочете зробити.

Деякі алгоритми ефективніші за допомогою простого структурованого програмування, а інші - краще орієнтуються на об'єкти.

Перед об'єктною орієнтацією багато шкіл навчають (редагувати) алгоритми проектування із структурованим програмуванням. Сьогодні багато шкіл навчають об’єктно-орієнтованого програмування, ігноруючи дизайн та ефективність алгоритмів ..

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


0

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

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

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


0

Хороший об’єктно-орієнтований дизайн допоміг мені значно пришвидшити додаток. Доводилося генерувати складну графіку алгоритмічно. Я зробив це через автоматизацію Microsoft Visio. Я працював, але був неймовірно повільним. На щастя, я вставив додатковий рівень абстракції між логікою (алгоритмом) та матеріалами Visio. Мій компонент Visio виявив свою функціональність через інтерфейс. Це дозволило мені легко замінити повільний компонент іншим, створюючи SVG-файли, що було принаймні в 50 разів швидше! Без чистого об'єктно-орієнтованого підходу коди алгоритму та контролю Vision були б заплутані таким чином, що перетворило б зміни в кошмар.


ви мали на увазі дизайн OO, застосований із процедурною мовою, або мова програмування OO Design & OO?
umlcat

Я кажу про додаток C #. І дизайн, і мова є OO. Якщо OO-iness мови введе деякі невеликі хіти продуктивності (виклики віртуального методу, створення об'єктів, доступ членів через інтерфейс), дизайн OO допоміг мені створити набагато швидший додаток. Що я хочу сказати: Забудьте про хіти продуктивності завдяки OO (мова та дизайн). Якщо ви не робите важких розрахунків з мільйонами повторень, ОО не завдасть вам шкоди. Там, де ви зазвичай втрачаєте багато часу, це введення / виведення.
Олів'є Якот-Дескомб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.