Чистий читабельний код проти швидко важкого для читання коду. Коли перейти лінію?


67

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

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

Коли це нормально, щоб перетнути цю лінію?


69
Ви відповіли на власне запитання, ви переходите лінію, коли вам потрібно перейти лінію
gnibbler

6
Крім того, ваш "брудний код" може працювати так само швидко, як "чистий код" на апараті через 6 місяців. Не перебирайте за борт, як це робив Windows. :)
Mateen Ulhaq

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

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

1
Якщо мова йде про компілятори в ці дні, ваш потворний код, швидше за все, буде таким самим, як і ваш чистий код (якщо припустити, що ви не робите справді дивних речей). Особливо в .NET, це не так, як C ++ / MFC дні, де те, як ви визначаєте свої змінні, матиме вплив на продуктивність. Напишіть кодекс, що підтримується. якийсь код просто виявиться складним, але це не означає, що він некрасивий.
DustinDavis

Відповіді:


118

Ви переходите лінію, коли

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

Ось приклад із реального світу: експериментальна система, якою я працюю, надто повільно створювала дані, займаючи понад 9 годин за цикл та використовуючи лише 40% процесора. Замість того, щоб занадто заплутати код, я перемістив усі тимчасові файли у файлову систему пам'яті. Додано 8 нових рядків некрасивого коду, і тепер використання процесора вище 98%. Проблема вирішена; ніякої потворності не потрібно.


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

4
@PaulR Як зберегти цей код? У формі коментарів? Це неправильно, іммо - коментарі застаріли, ніхто їх не читає, і особисто, якщо я бачу коментований код, я зазвичай його видаляю - саме для цього керує джерело. Коментар до способу, що пояснює, що він робить, краще, imo.
Євген

5
@Eugene: Зазвичай я зберігаю оригінальну версію розпорядження з іменем fooта перейменовую її foo_ref- як правило, вона живе безпосередньо вище fooу вихідному файлі. У моїй тестовій упряжі я викликаю fooі foo_refдля перевірки і вимірювання відносної продуктивності.
Пол Р

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

57

Це помилкова дихотомія. Ви можете зробити код швидко і просто в обслуговуванні.

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

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

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

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

На мій досвід, саме тут починається багато програм.


Я згоден. Справді, швидкий нечистий код з часом стає повільніше, оскільки ви не зможете правильно його змінити.
edA-qa mort-ora-y

22
Я не згоден, це хибна дихотомія; IMO є сценарії, особливо в коді бібліотеки (не стільки в коді програми ), коли розкол дуже реальний. Дивіться мою відповідь для отримання додаткової інформації.
Марк Гравелл

1
Марк, можна відповісти на відповіді в коментарях із URL-адресою "посилання". programmers.stackexchange.com/questions/89620/…

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

Я не згоден, що це хибна дихотомія. Оскільки я роблю багато графічної роботи, найочевидніший приклад для мене - це тісний графічний цикл: я не знаю, як часто це все-таки робиться, але це було звичайним явищем для ігрових двигунів, написаних на C, щоб використовувати асамблею для основного візуалізації петлі, щоб вичавити кожну останню краплю швидкості. Це також змушує мене думати про ситуації, коли ви програмуєте в Python, але використовуєте модулі, написані на C ++. "Важко читати" завжди відносно; щоразу, коли ви переходите на мову нижчого рівня для швидкості, цей код читати важче, ніж решта.
поштовх

31

В моєму існуванні OSS я роблю багато бібліотечної роботи, спрямованої на продуктивність, тобто глибоко пов'язану з структурою даних абонента (тобто зовнішньою для бібліотеки), не маючи (за задумом) мандат на вхідні типи. Тут найкращим способом зробити цього виконавця є метапрограмування, яке (оскільки я перебуваю на .NET-land) означає IL-emit. Це якийсь потворний, потворний код, але дуже швидкий.

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

"кодування над скелею божевілля, тому вам не доведеться "

Тепер код програми дещо інший, тому що там, де "звичайні" (розумні) розробники зазвичай вкладають велику частину свого спільного / професійного часу; цілі та очікування кожного (ІМО) дещо різні.

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


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

6
@tdammers дійсно, і я намагаюся зробити це якомога далі; але це трохи схоже на нанесення помади на свиню.
Марк Гравелл

1
Ну, можливо, слід розрізняти потворний синтаксис та потворні алгоритми - потворні алгоритми іноді необхідні, але потворний синтаксис, як правило, недоцільний ІМО.
tdammers

4
Потворний синтаксис @IMO досить неминучий, якщо те, що ви робите, це за своєю природою кілька рівнів абстракції під звичайним рівнем мови.
Марк Гравелл

1
@marc ... цікаво. Моя перша реакція на те, що мета / абстракція була некрасивою, була підозра на те, що конкретна мова / форма форми не сприяє мета-кодуванню, а не якийсь основний закон, що з'єднує їх два. Що змусило мене повірити, що це був приклад прогресивних метарівнів у математиці, що закінчуються теорією множин, вираз якої навряд чи менший за алгебру чи навіть конкретну арифметику. Але потім встановити-нотація, ймовірно, іншу мову в цілому і кожен рівень abstration під свою мову ....
explorest

26

Профілювавши код і переконавшись, що він насправді викликає значне уповільнення.


3
А що "значуще"?
Грак

2
@ hotpaw2: це розумна відповідь - передбачається, що розробники принаймні дещо конкурентні. Інакше так, використовувати щось швидше, ніж сортування бульбашок - це, як правило, хороша ідея. Але занадто часто хтось буде (щоб продовжувати сортування) поміняти хуйсорт на купе на 1% різниці, тільки щоб побачити, як хтось інший поміняв його назад через шість місяців з тієї ж причини.

1
Ніколи не існує причини робити нечистий код. Якщо ви не можете зробити свій ефективний код чистим і простим у обслуговуванні, ви робите щось не так.
edA-qa mort-ora-y

2
@SF. - замовник завжди знайде це занадто повільно, якщо може бути швидшим. Він не піклується про "чистоту" коду.
Грак

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

13

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

Писати "брудний" код у спробі зробити його швидше, мабуть, нерозумно, оскільки ви точно не знаєте, що ваші зміни насправді щось покращують. Кнут висловив найкраще:

"Ми повинні забути про малу ефективність, скажімо, про 97% часу: передчасна оптимізація - корінь усього зла . І все ж, ми не повинні передавати свої можливості в ті критичні 3%. Хороший програміст не буде принаджений таким поступливістю міркуючи, він буде розумним уважно переглянути критичний код, але лише після того, як цей код буде визначений ".

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


10

Оскільки в запитанні написано "швидко важко читати код", проста відповідь ніколи не буває. Ніколи не є приводом для написання коду, який важко читати. Чому? Дві причини.

  1. Що станеться, якщо сьогодні ввечері на шляху додому потрапить автобус? Або (більш оптимістично і більш типово) зняли цей проект і переписали на щось інше? Невелика користь, яку ви уявляєте, що ви зробили завдяки своєму заплутаному безладному коду, повністю переважає той факт, що ніхто інший не може це зрозуміти . Ризик, який це створює для програмних проектів, важко переоцінити. Я працював один раз з великою АТСвиробник (якщо ви працюєте в офісі, напевно у вас на столі один із їхніх телефонів). Їхній керівник проекту одного разу сказав мені, що їх основний продукт - фірмове програмне забезпечення, яке перетворило стандартну коробку Linux у повнофункціональну телефонну станцію - всередині компанії було відоме як "кльош". Ніхто вже не розумів цього. Кожен раз, коли вони реалізовували нову функцію. вони натиснули компіляцію, потім відступили, закрили очі, порахували до двадцяти, а потім зазирнуть у пальці, щоб побачити, чи працює. Жоден бізнес не потребує основного продукту, який вони більше не контролюють, але це страхітливий загальний сценарій.
  2. Але мені потрібно оптимізувати! Гаразд, тому ви дотримувались всіх чудових порад в інших відповідях на це запитання: ваш код провалює свої тестові кейси щодо ефективності, ви ретельно профайлювали його, визначили вузькі місця, придумали рішення ... і це буде залучити трохи біт-подвій . Чудово: тепер ідіть і оптимізуйте. Але ось секрет (і ви, можливо, захочете сісти за цей): оптимізація та зменшення розміру вихідного коду - це не одне і те ж. Коментарі, білий пробіл, дужки та значущі назви змінних - це величезні допоміжні засоби для читабельності, які вам нічого не коштують, оскільки компілятор викине їх. (Або якщо ви пишете некомпільовану мову на зразок JavaScript - і так, є дуже вагомі причини оптимізації JavaScript - з ними можна вирішити компресор .) Довгі рядки тісного, мінімалістичного коду (як той, який має muntoo розміщені тут ) не мають нічого спільного з оптимізацією: це програміст, який намагається показати, наскільки вони розумні, упакувавши якомога більше коду в якомога менше символів. Це не розумно, це дурно. Справді розумний програміст - це той, хто може чітко донести свої ідеї до інших.

2
Я не можу погодитися, що відповідь "ніколи". Деякі алгоритми по суті дуже важко зрозуміти та / або ефективно реалізувати. Читання коду, незалежно від кількості коментарів, може бути дуже складним процесом.
Рекс Керр

4

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

Інакше це помилкова дихотомія - якщо ви думаєте, що вам потрібно зробити це некрасивим, щоб зробити це швидше, ви робите це неправильно. (Або ваші принципи щодо того, що таке хороший код, потрібно переглянути. Використовувати goto насправді досить елегантно, коли це правильне рішення проблеми. Однак це рідко.)


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

Я не згоден, це хибна дихотомія; IMO є сценарії, особливо в коді бібліотеки (не стільки в коді програми), де розкол дуже реальний. Дивіться мою відповідь для отримання додаткової інформації.
Марк Гравелл

@mark, якщо "інший грамотний програміст" справді компетентний, то і код викидання не повинен бути проблемою :)

@Mark - Легко. Просто напишіть код викидання, щоб він не пройшов будь-якого виробничого тестування, можливо, якимось незмінним чином.
hotpaw2

@ Марк, якщо ваш «викинутий код» робить його виробництвом, то це не викинутий код. Зауважте, що я взяв час у своїй відповіді, щоб уточнити, що я кажу про код, який буквально викидається: тобто видалити після першого використання. Інакше я погоджуюся з вашими почуттями і сказала стільки ж у своїй відповіді.
мааку

3

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

Люди все ще роблять скручені ручним кодом SSE / NEON / тощо. збірка, щоб спробувати перемогти програмне забезпечення конкурента на популярній цьогоріч чіпі процесора.


Хороша бізнес-перспектива, іноді програмістам потрібно виходити за рамки лише технічного.
this.josh

3

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

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


0

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

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

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

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

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

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

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