Чи добре замінити оптимізований код на читабельний код?


78

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

Це гарна ідея замінити його сучасним кодом?

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

Здається, компілятори також стають кращими, тому такі речі, як struct abc = {}мовчки, перетворюються на memsets, shared_ptrs в значній мірі створюють той самий код, що і подвійний вказівник вказівника, шаблони працюють надто добре, оскільки вони створюють супер худий код тощо.

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

Це гарна ідея змінити такий код, якщо вам доведеться торкнутися його невеликого шматочка?


20
Читання та оптимізація не протидіють більшості часу.
deadalnix

23
Чи можна покращити читабельність за допомогою деяких коментарів?
такиІнший користувач

17
Турбує те, що OOP-ification вважається «сучасним кодом»
Джеймс

7
як філософія slackware: якщо вона не зламана, не виправте її, принаймні у вас є дуже, дуже вагомі причини зробити це
osdamv

5
Під оптимізованим кодом ви маєте на увазі фактичний оптимізований код або так званий оптимізований код?
dan04

Відповіді:


115

Де?

  • На домашній сторінці веб-сайту в масштабі Google це неприпустимо. Зберігайте речі якомога швидше.

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

Взагалі, які нефункціональні вимоги до частини коду, над яким ви працюєте? Якщо дія повинна виконуватися менше 900 мс. у заданому контексті (машина, навантаження тощо) 80% часу, а насправді він виконує менше 200 мс. У 100% часу, звичайно, зробіть код більш читабельним, навіть якщо це може трохи вплинути на продуктивність. Якщо, з іншого боку, та сама дія ніколи не виконувалася протягом десяти секунд, ну, вам слід скоріше спробувати зрозуміти, що не так у виконанні (або в першу чергу вимога).

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


47
+1! Якщо у вас немає номерів, знайдіть кілька номерів. Якщо у вас немає часу отримати номери, ви не встигнете їх змінити.
Tacroy

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

6
Замість того, щоб додати ще одну відповідь, я поставив +1 цій відповіді. Якщо розуміння фрагментів коду є таким важливим, прокоментуйте їх добре. Я працював у середовищі C / C ++ / Assembly зі спадковим кодом десятиліття з десятками дописувачів. Якщо код працює, залиште його в спокої і поверніться до роботи.
Кріс К

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

36

Зазвичай ні .

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

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

Звичайно, профіль, профіль, профіль , особливо якщо це критична область шляху.


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

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

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

29

Якщо коротко: це залежить

  • Чи справді вам знадобиться або використовуєте свою реконструйовану / покращену версію?

    • Чи є конкретний виграш, негайний чи довгостроковий?
    • Це виграш лише для ремонту, чи справді архітектурний?
  • Чи справді її потрібно оптимізувати?

    • Чому?
    • До якого цільового прибутку потрібно прагнути?

У подробицях

Вам потрібні будуть очищені, блискучі речі?

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

Більш конкретно, знайте це:

Є таке поняття, як Over-Engineering

Це анти-шаблон, і він поставляється з вбудованими проблемами:

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

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

Вам це не потрібно

Принцип YAGNI не є повністю ортогональним з іншим питанням, але він допомагає задати собі питання: чи вам це потрібно?

Чи справді більш складна архітектура для вас приносить користь, окрім того, що виглядає як більш доглянута?

Якщо це не зламалося, не виправляйте це

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

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

  • насправді так,
  • насправді потрібно робити,
  • врешті-решт потрібно буде зробити,
  • і як добре це робить.

Чи справді її потрібно оптимізувати?

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

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

Також остерігайтеся невидимих ​​характеристик. Швидше за все, ваша "розширювана" версія цього коду ви також збільшите більше пам’яті під час виконання, і представите ще більший слід статичної пам'яті для виконуваного файлу. Блискучі функції OO мають такі неінтуїтивні витрати, як ці, і вони можуть мати значення для вашої програми та середовища, на якому вона повинна працювати.

Міряти, міряти, міряти

Як зараз Google, це все про дані! Якщо ви можете створити резервну копію даних, то це необхідно.

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

Зміни впливають на багато речей:

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

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

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


Загалом...

Так, але...

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

У ідеальному світі це правильне рішення:

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

На практиці:

  • ви можете зробити це гірше

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

  • Ви не можете передбачити майбутнє

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

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

Зробіть це частиною процесу

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

Якщо у вас є процес вирішення таких рішень, ви приймаєте їх особисту перевагу:

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

Покриття тестування, профілювання та збір даних є складними

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

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

Огляди коду - це тестування передпокою Команди розвитку

І я думаю, що остання частина тут є ключовою: робіть огляди коду. Ви не знаєте цінності ваших удосконалень, якщо зробите їх сольними. Огляди коду - це наше «тестування на коридорі»: дотримуйтесь версії Закону Лінуса Реймонда як щодо виявлення помилок, так і для виявлення над інженерних та інших анти-шаблонів, а також для того, щоб код відповідав можливостям вашої команди. Немає сенсу мати "найкращий" код, якщо ніхто інший, крім вас, не може зрозуміти і підтримувати його, і це стосується як критичних оптимізацій, так і 6-шарових глибоких архітектурних конструкцій.

Як заключні слова, пам’ятайте:

Всім відомо, що налагодження вдвічі складніше, ніж написання програми в першу чергу. Отже, якщо ви настільки розумні, як ви можете бути, коли пишете це, як ви коли-небудь налагодите це? - Брайан Керніган


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

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

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

@MiyamotoAkira: короткі пропозиції та висловлювання думок не можуть висловити багато чого. Вони мають на меті бути у вас в обличчі, і вони повинні бути розвинені на стороні. Я сам дуже прихильний до того, щоб якомога частіше переглядати та торкатися коду, навіть якщо часто без великої безпеки або без особливих причин. Якщо він брудний, ви його очищаєте. Але, так само, я також кілька разів опік. І все одно згорять. Поки це не студенти 3-го ступеня, я не заперечую надто сильно, поки що це завжди були короткочасні опіки для довготривалих вигод.
haylem

8

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

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

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

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


8

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

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

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

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

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


6

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

також переконайтеся, що код поводиться однаково з відповідними тестами


5

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

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


4

В основному, ви запитуєте, чи рефакторинг - це корисна справа. Відповідь на це, безумовно, так.

Але ...

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

Про це написав книгу Мартін Фаулер .


3

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

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


3

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

#ifdef READABLE_ALT_IMPLEMENTATION

   double x=0;
   for(double n: summands)
     x += n;
   return x;

#else

   auto subsum = [&](int lb, int rb){
          double x=0;
          while(lb<rb)
            x += summands[lb++];
          return x;
        };
   double x_fin=0;
   for(double nsm: par_eval( subsum
                           , partitions(n_threads, 0, summands.size()) ) )
     x_fin += nsm;
   return x_fin;

#endif

3

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

  1. Шукайте тест на продуктивність та підтримуючу інформацію про профілювання. Якщо немає перевірки ефективності, то те, що можна стверджувати без доказів, можна відхилити без доказів. Зауважте, що ваш сучасний код швидший, і видаліть старий код. Якщо хтось сперечається (навіть ви самі), попросіть його написати код профілювання, щоб довести, що швидше.
  2. Якщо код для профілювання існує, все одно напишіть сучасний код. Назвіть це щось на кшталт <function>_clean(). Потім "змагайтеся" зі своїм кодом проти поганого коду. Якщо ваш код краще, видаліть старий код.
  3. Якщо старий код швидше, залиште там свій сучасний код. Він служить гарною документацією для того, що має на увазі робити інший код, і оскільки код "гонки" є там, ви можете продовжувати його виконувати для документування характеристик продуктивності та відмінностей між двома шляхами. Ви також можете провести тест на відмінності в поведінці коду. Важливо, що сучасний код обіграє «оптимізований» код одного дня, гарантовано. Потім ви можете видалити неправильний код.

QED.


3

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

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

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

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


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

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

2

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

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

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


1
Один з хлопців, з яким я зараз працюю, любить оптимізувати речі в тих сферах, на які користувачі потрапляють раз на місяць, якщо це часто. Це вимагає часу і нечасто викликає інші проблеми, тому що він любить кодувати і виконувати, і нехай QA або інша функція вниз за течією фактично перевіряється. : / Справедливо кажучи, він, як правило, швидкий, швидкий та акуратний, але ці "копійки-копійки" просто ускладнюють справи для решти команди та їх постійна смерть була б хорошою справою.
DaveE

@DaveE: Чи застосовуються ці оптимізації через проблеми чи ефективність? Або цей розробник робить це лише тому, що може? Я думаю, якщо ви знаєте, що оптимізація не матиме впливу, ви можете сміливо замінити їх на більш читабельний код, але я би довіряв лише тому, хто є фахівцем у цій системі.
FrustratedWithFormsDesigner

Вони зробили, бо він може. Насправді він зазвичай економить деякі цикли, але коли взаємодія користувача з програмним елементом займає деяку кількість секунд (від 15 до 300-іш), гоління десятої частини секунди тривалості в досягненні "ефективності" нерозумно. Тим більше, коли людям, які йдуть за ним, потрібно зайняти реальний час, щоб зрозуміти, що він зробив. Це додаток PowerBuilder, спочатку побудований 16 років тому, тому зважаючи на генезис речей, можливо, розумний розум, але він відмовляється оновити свій розум до поточної реальності.
DaveE

@DaveE: Я думаю, я більше погоджуюся з хлопцем, з яким ти працюєш, ніж ти. Якщо мені не дозволяють виправляти речі, які повільно, абсолютно не з вагомих причин, я зійду з розуму. Якщо я бачу рядок C ++, який неодноразово використовує оператор + для збирання рядка або коду, який щоразу відкривається і читає / dev / urandom через цикл тільки тому, що хтось забув встановити прапор, то я його виправляю. Будучи фанатичним щодо цього, мені вдалося зберегти швидкість, коли інші люди дозволили б ковзати по одній мікросекунді за один раз.
Зан Лінкс

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

2

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

І "худорлявий" нечитабельний код не завжди оптимізований.

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


2

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

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

ПРИМІТКА. Оптимізований код можна зробити читабельним за допомогою належного коментування, але не можна зробити читаний код оптимізованим.


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

2

Ось приклад, щоб побачити різницю між простим кодом та оптимізованим кодом: https://stackoverflow.com/a/11227902/1396264

наприкінці відповіді він просто замінює:

if (data[c] >= 128)
    sum += data[c];

з:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

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

Це виконується менше ніж за чверть початкового часу (11,54 сек проти 2,5 сек)


1

Тут головне питання: чи потрібна оптимізація?

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

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

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


1

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

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


0

Читання IMHO важливіше оптимізованого коду, оскільки в більшості випадків мікрооптимізація цього не варта.

Стаття про безглузді мікрооптимізації :

Як і більшість із нас, я втомився читати публікації в блозі про нечутливі мікрооптимізації, такі як заміна друку на ехо, ++ $ i на $ i ++ або подвійні цитати на одиничні лапки. Чому? Тому що 99,999999% часу, це не має значення.

"print" використовує ще один опкод, ніж "echo", оскільки він фактично щось повертає. Можна зробити висновок, що відлуння швидше, ніж друк. Але одна опкод нічого не коштує, насправді нічого.

Я спробував свіжу установку WordPress. Сценарій зупиняється, перш ніж він закінчиться "Помилкою шини" на моєму ноутбуці, але кількість опкодів вже перевищила 2,3 мільйона. Достатньо сказано.


0

Оптимізація відносна. Наприклад:

Розглянемо клас з купою членів BOOL:

// no nitpicking over BOOL vs bool allowed
class Pear {
 ...
 BOOL m_peeled;
 BOOL m_sliced;
 BOOL m_pitted;
 BOOL m_rotten;
 ...
};

Можливо, ви б спокусилися перетворити поля BOOL у біт-поля:

class Pear {
 ...
 BOOL m_peeled:1;
 BOOL m_sliced:1;
 BOOL m_pitted:1;
 BOOL m_rotten:1;
 ...
};

Оскільки BOOL є typedef'd як INT (що на платформах Windows є підписаним 32-бітовим цілим числом), це займає шістнадцять байтів і пакує їх в одне ціле. Це 93% економія! Хто міг скаржитися на це?

Це припущення:

Оскільки BOOL є typedef'd як INT (що на платформах Windows є підписаним 32-бітовим цілим числом), це займає шістнадцять байтів і пакує їх в одне ціле. Це 93% економія! Хто міг скаржитися на це?

призводить до:

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

Що раніше було

 push [ebx+01Ch]      ; m_sliced
 call _Something@4    ; Something(m_sliced);

стає

 mov  ecx, [ebx+01Ch] ; load bitfield value
 shl  ecx, 30         ; put bit at top
 sar  ecx, 31         ; move down and sign extend
 push ecx
 call _Something@4    ; Something(m_sliced);

Версія бітфілда більша на дев'ять байт.

Давайте сядемо і зробимо якусь арифметику. Припустимо, що до кожного з цих польових полів звертається шість разів у ваш код, три рази для запису та три рази для читання. Вартість зростання коду становить приблизно 100 байт. Це не буде рівно 102 байти, тому що оптимізатор може мати можливість скористатися значеннями, які вже є в регістрах для деяких операцій, а додаткові інструкції можуть мати приховані витрати з точки зору зменшеної гнучкості реєстру. Фактична різниця може бути більшою, вона може бути меншою, але для обчислення зворотного конверту назвемо це 100. Тим часом економія пам'яті становила 15 байт на клас. Тому точка беззбитковості - сім. Якщо ваша програма створює менше семи екземплярів цього класу, тоді вартість коду перевищує економію даних: Ваша оптимізація пам'яті була деоптимізацією пам'яті.

Список літератури

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