Чому так багато розробників вважають, що продуктивність, читабельність та ремонтопридатність не можуть співіснувати?


34

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

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

Хоча я визнаю, що існують надзвичайні випадки, чому багато розробників наполягають, що ефективна програма / дизайн призведе до поганої читабельності та / або поганої ремонтопридатності, і, отже, продуктивність не повинна враховувати дизайн?


9
Міркувати про це було б майже неможливо, але для невеликих фрагментів коду це цілком очевидно. Просто порівняйте читабельну та ефективну версії, скажімо, швидкості.
SK-логіка

7
Му. Спершу слід підтримати своє твердження, що багато розробників наполягають на тому, що ефективність призводить до неможливості.
Пітер Тейлор

2
Логіка SK: На мою думку, це одна з найкращих частин усіх сайтів обміну стаксом, оскільки тут виникає сумнів у тому, що очевидно, що може бути здоровим раз у раз. Те, що може бути очевидним для вас, може бути не очевидним для когось іншого, і навпаки. :) Обмін - це турбота.
Андреас Йохансон

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

2
-1 за запитання. Коли я прочитав це, я подумав, що це солом'яна людина, щоб виселити єдину правдиву відповідь: "Тому що вони не використовують пітона".
Інго

Відповіді:


38

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

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


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

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

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

Моя відповідь ... Більшість розробників погано
справляються

38

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

  • Не передчасно песимізуйте. Коли у вас є вибір між двома конструкціями, рівними за складністю, виберіть той, який має найкращі експлуатаційні характеристики. Один з відомих прикладів С ++ - це поширеність післякріплення лічильників (або ітераторів) у циклах. Це абсолютно непотрібна передчасна песимізація, яка МОЖЕ вам нічого не коштувати, але це МОЖЛИВО, тому не робіть цього.
  • У багатьох випадках у вас поки що немає жодної справи, щоб пройти поблизу мікрооптимізації. Алгоритмічні оптимізації - це плід нижчого рівня, і його майже завжди набагато простіше зрозуміти, ніж насправді оптимізації низького рівня.
  • Якщо і ТІЛЬКИ, якщо продуктивність абсолютно критична, ви збиваєтеся та забруднюєтесь. Насправді ви ізолюєте код стільки, скільки можете спочатку, а потім ви впадете і забрудниться. І там все стає брудно, із схемами кешування, лінивою оцінкою, оптимізацією макета пам’яті для кешування, блоками вбудованих вбудованих елементів або складанням, шарами шарів шарів тощо. шкодити, якщо вам доведеться виконати будь-яке технічне обслуговування цього коду, але вам доведеться, тому що продуктивність дуже важлива. Редагувати: До речі, я не кажу, що цей код не може бути красивим, і він повинен бути таким красивим, як він може бути, але він все ще буде дуже складним і часто вивернутим порівняно з менш оптимізованим кодом.

Налаштуйся правильно, красивіше, швидко діставай. У тому порядку.


Мені подобається велике правило: «красиво, швидше. У такому порядку ". Я почну це використовувати.
Мартін Йорк

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

@KeithB - ​​ви добре зробите свою думку, я додам це до своєї відповіді.
Joris Timmermans

+1: "Налаштуйся, будь красивим, швидше. У такому порядку". Дуже приємне резюме, з яким я згоден на 90%. Іноді я можу виправити певні помилки (виправдати це) лише раз, коли мені це стане красивим (і більш зрозумілим).
Джорджіо

+1 за "Не передчасно песимізуйте". Порада уникати передчасної оптимізації - це не дозвіл несанкціоновано використовувати алгоритми, котрі мають голову. Якщо ви пишете Java, і у вас є колекція, яку ви будете containsбагато телефонувати , використовуйте, а HashSetне an ArrayList. Виступ може не мати значення, але причин цього не робити. Використовуйте відповідність між хорошим дизайном та робочими характеристиками - якщо обробляти якусь колекцію, спробуйте зробити все за один прохід, який, мабуть, буде і більш читабельним, і швидшим (напевно).
Том Андерсон

16

Якщо я можу припустити "запозичити" приємну схему @ greengit і зробити невелике доповнення:

|
P
E
R
F
O  *               X <- a program as first written
R   * 
M    *
A      *
N        *
C          *  *   *  *  *
E
|
O -- R E A D A B I L I T Y --

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

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


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

2
+1 для "Більшість програм мають багато можливостей для вдосконалення в усіх аспектах".
Стівен

5

Саме тому, що високоефективні компоненти програмного забезпечення, як правило, на порядок складніші, ніж інші компоненти програмного забезпечення (всі інші речі рівні).

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

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

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

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


3

Не стільки, що ці речі не можуть співіснувати. Проблема полягає в тому, що код кожного є повільним, нечитабельним і нездійсненним на першій ітерації. Решту часу витрачається на роботу над вдосконаленням найважливішого. Якщо це продуктивність, то займіться цим. Не пишіть жахливо жахливий код, але якщо він просто повинен бути X швидким, то зробіть це X швидким. Я вважаю, що продуктивність та чистота в основному не пов'язані між собою. Код виконавця не викликає некрасивого коду. Однак якщо ви витрачаєте свій час на налаштування кожного шматочка коду, щоб бути швидким, здогадайтеся, на що ви не витратили свій час? Зробити ваш код чистим та ретельним.


2
    |
    П
    Е
    R
    Ж
    O *
    R * 
    М *
    A *
    N *
    C * * * * *
    Е
    |
    O - ПРОЧИТАННЯ -

Як ти бачиш...

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

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


1

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


1

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

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

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

Сенс у тому, щоб не турбуватися про дрібниці, які могли б покращити справи. Скористайтеся профілером і переконайтесь, що 1) те, що у вас зараз, - це проблема, і 2) те, що ви змінили, було вдосконаленням.


1

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

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


1

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

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

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

ДОБАВЛЕНО: Ще в давні часи вартість одного комп’ютера становила мільйони, тому оптимізація часу процесора була дуже важливою. Тоді вартість розробки та обслуговування коду стала більшою, ніж вартість комп'ютерів, тому оптимізація вийшла з користі порівняно з продуктивністю програміста. Однак тепер інша вартість стає більшою, ніж вартість комп'ютерів, вартість харчування та охолодження всіх цих центрів обробки даних тепер стає більшою, ніж вартість усіх процесорів всередині.


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

1

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

Мікроефективність

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

Тож ця ідея, що алгоритмічна оптимізація завжди козирна, скажімо, оптимізація, пов'язана з моделями доступу до пам’яті, - це завжди те, з чим я не дуже погоджувався. Звичайно, якщо ви використовуєте міхурний сорт, жодна кількість мікрооптимізації не може вам тут допомогти ... але всередині розуму, я не думаю, що це завжди так чітко. І, певно, алгоритмічну оптимізацію важче підтримувати, ніж мікрооптимізацію. Мені було б набагато легше підтримувати, скажімо, Embree Embree, який використовує класичний і простий алгоритм BVH і просто мікронастроює лайно з нього, ніж код OpenVDB Dreamwork для передових шляхів алгоритмічного прискорення моделювання рідини. Тож хоча б у своїй галузі я хотів би бачити більше людей, які знайомі з мікрооптимізацією архітектури комп’ютера, як Intel, коли вони вийшли на сцену, на відміну від створення тисяч і тисяч нових алгоритмів та структур даних. Завдяки ефективним мікрооптимізаціям люди можуть потенційно знайти все менше і менше причин для створення нових алгоритмів.

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

Функціональні мови

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

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

Читання та ремонтопридатність

Як уже було сказано, я вважаю, що читабельність та ремонтопридатність не є гармонійними поняттями. Зрештою, найбільш читаний код для більшості з нас смертних дуже інтуїтивно відображає людські думки, а людські думки мають властиві помилки: " Якщо це станеться, зробіть це. Якщо це станеться, зробіть це. Інакше зробіть це. На жаль , Я щось забув! Якщо ці системи взаємодіють одна з одною, це має статися, щоб ця система могла це зробити ... о, зачекайте, що з цією системою, коли ця подія запускається?"Я забув точну цитату, але хтось одного разу сказав, що якби Рим був побудований як програмне забезпечення, знадобиться лише птах, що висаджується на стіну, щоб її знищити. Такий випадок з більшою частиною програмного забезпечення. Це більш крихко, ніж ми часто дбаємо про те, щоб Подумайте. Декілька рядків, здавалося б, нешкідливих кодів тут і там можуть зупинити його до того, щоб змусити нас переглянути весь дизайн, а мови високого рівня, які мають на меті бути максимально читабельними, не є винятком із таких помилок дизайну людини. .

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


1
"Мікроефективність" начебто каже: "Тут немає такого поняття, як доступ до пам'яті O (1)"
Caleth

0

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

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


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

0

Тому що досвідчені програмісти дізналися, що це правда.

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

Ми працювали над численним кодом, який для вирішення проблем продуктивності ДУЖЕ складний.

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


0

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

Ось найвідоміший я думаю. Взятий з Quake III Arena та приписуваний Джону Кармаку, хоча, я думаю, було кілька ітерацій цієї функції, і вона спочатку не була створена ним ( хіба Вікіпедія не чудова? ).

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
    //      y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

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