Яка найсмішніша песимізація, яку ви бачили? [зачинено]


145

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


21
«Песимізація» - чудове слово.
mqp

На випадок, якщо ви цього не знали, вони розповіли про вашу нитку тут, на останньому подкасті.
mmcdole

Відповіді:


81

На старому проекті ми успадкували деяких (інакше чудових) програмістів з вбудованими системами, які мали величезний досвід Z-8000.

Нашим новим середовищем був 32-розрядний Sparc Solaris.

Один з хлопців пішов і поміняв усі інти на шорти, щоб пришвидшити наш код, оскільки захоплення 16 біт оперативної пам’яті було швидше, ніж схоплення 32 біта.

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


16
Гей, де ти навчився математиці? 2 інструкції з доступом до кешу / оперативної пам'яті, очевидно, швидше, ніж 1 інструкція з 1 кешем / оперативною пам'яттю!
Бритва Шторм

2
@RazorStorm На пізніших машинах, де пропускна здатність та кеш-пам'ять є більш цінними, буде навпаки. Бітмаска / зсув коштує дешево, але ви хочете якомога більше вмістити в кеш, а також мінімізувати пропускну здатність.
Джед

206

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

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

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

  • Читання одного і того ж ключа реєстру тисячі (або 10 тисяч) разів під час запуску програми.
  • Завантаження однієї і тієї ж DLL сотні чи тисячі разів
  • Витрата мегабайтів пам'яті, зберігаючи повний шлях до файлів
  • Не впорядковуючи структури даних, щоб вони зайняли набагато більше пам'яті, ніж потрібно
  • Розміщення всіх рядків, які зберігають імена файлів або шляхи до MAX_PATH
  • Безкоштовне опитування за те, що має події, зворотній зв'язок або інші механізми сповіщення

Я вважаю, що кращим є таке твердження: "оптимізація без вимірювання та розуміння зовсім не оптимізація - її просто випадкова зміна".

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


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

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

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

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

5
Якщо це надто зловживає, то населення, яке ставить запитання щодо ПС, сильно зважується до людей, що вижили. : D
dkretz

114

Бази даних - це поле для песимізації.

У вибране:

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

Це з моєї голови.


Протистояти необхідності індексувати просто боляче.
Білл Ящірка

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

Так, я думаю, кожен DBA, мабуть, в певний момент зійшов з об'єднання з маршрутом архівної таблиці. Це завжди здається таким розумним.
Cruachan

3
Додаю: розділіть базу даних на декілька різних баз даних (клієнти змінного струму, клієнти df тощо)
Gabriele D'Antona,

Не могли б ви детальніше зупинитися на "Denormalize into polja as-an-array."? Що ти тут маєш на увазі?
Барт ван Хекелом

87

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

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

З іншого боку, наші архітектори програмного забезпечення вирішили, що розпаковані дані повинні бути відформатовані у дуже читабельний XML-документ і зберігатися в нашій базі даних як такий (на відміну від того, щоб кожне поле зберігалося у відповідному стовпчику). Їх ідея полягала в тому, що "XML - це майбутнє", "дисковий простір дешевий" та "процесор дешевий", тому не потрібно було нічого оптимізувати. Результатом було те, що наші 16-байтові пакети були перетворені на 2 КБ документів, що зберігаються в одному стовпчику, і для навіть простих запитів нам довелося завантажувати мегабайти XML-документів у пам'ять! Ми отримували понад 50 пакетів в секунду, тож ви можете собі уявити, наскільки жахливою стала вистава (BTW, компанія збанкрутувала).

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


37
Я погоджуюсь, що "процесорний / дисковий простір / пам'ять дешева" - це справжній корінь усього зла. +1
ksuralta

5
Я також чув, що XML-драйв теж. Ще одна танкована компанія.
n8wrl

19
@ksuralta: "Простір процесора / диска / пам'ять дешевий" - це зручне привід для уникнення думки. Уникнення думки - це уявний корінь усього зла.
Пісквор вийшов з будівлі

Ця XMLization трапилася і на моєму робочому місці, після чого послідувала JSONization. Все, щоб уникнути певного «кропіткого» дизайну реляційних баз даних.
Tanz87

75

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

Ось останній приклад:

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

Я: Чому ти це вважаєш? Яку проблему ви намагаєтеся вирішити?

Йому: Таблиця X занадто широка, ми розділяємо її з міркувань продуктивності.

Я: Що змушує вас вважати це занадто широким?

Він: Консультант сказав, що це занадто багато стовпців, щоб їх було в одній таблиці.

Я: І це впливає на продуктивність?

Він: Так, користувачі повідомили про періодичні уповільнення у модулі XYZ програми.

Я: Як ви знаєте, ширина таблиці є джерелом проблеми?

Йому: Це ключова таблиця, використовувана модулем XYZ, і вона подібна до 200 стовпців. Має бути проблема.

Я (пояснення): Але модуль XYZ, зокрема, використовує більшість стовпців цієї таблиці, а стовпці, які він використовує, непередбачувані, оскільки користувач налаштовує додаток для відображення даних, які вони хочуть відображати з цієї таблиці. Цілком ймовірно, що 95% часу ми з вами все-таки приєднаємось до всіх столів разом, що завдасть шкоди продуктивності.

Він: Консультант сказав, що він занадто широкий, і нам потрібно це змінити.

Я: Хто цей консультант? Я не знав, що ми найняли консультанта, і взагалі не спілкувались із командою розвитку.

Він: Ну, ми їх ще не найняли. Це частина пропозиції, яку вони запропонували, але вони наполягали на тому, що нам потрібно реструктуризувати цю базу даних.

Я: А- а-а. Тож консультант, який продає послуги з перероблення баз даних, вважає, що нам потрібно перепроектувати базу даних ....

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

  1. Відсутні індекси в кількох ключових стовпцях.
  2. Кілька негідних аналітиків даних, які періодично блокували ключові таблиці (включаючи "занадто широку"), запитуючи виробничу базу даних безпосередньо з MSAccess.

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


Aaag MSAccess to prod. Ми написали процедуру, щоб відмовитись від усіх підключень доступу кожні кілька хвилин, і нарешті отримали повідомлення, що це погано.
Nat

1
У нас була аналогічна робота, але вона вийшла з ладу. Справедливості, Access не є проблемою, він просто спрощує неофітів створювати / запускати непрофільні запити.
JohnFx

У нас є залежність у нашої компанії від застарілих підключень ad hoc до виробничої БД. Нічого подібного до кількох випадкових SQL'ers, щоб забути пункт WHERE і зупинити основні таблиці!
HardCode

35
«Я чув, що у маува є найбільше оперативної пам’яті»
Пісквор вийшов з будівлі

Це могло бути і гірше. Редактор запитів Excel блокує повну базу даних при її використанні. Коли я не знав про це, я залишив один примірник відкритим на більшу частину дня, поки я працював у чомусь іншому. Найгірше, що MS SQL Server не повідомив правильне ім’я користувача / машину, яка робила блокування. Через години я зрозумів, що я був причиною блокування через те, що заблоковані таблиці були частиною перегляду, який я запитував і спочатку перевірив усе інше.
Естебан Кюбер

58

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

Зовсім песимістично!


10
можливо, вони намагалися
вбудувати

6
Отже, в основному єдиним новим принципом є замість того, щоб потужність генерувалась відносним рухом провідників і потоків, що його виробляла модифікаційна взаємодія магнітобажання та ємнісної тривалості?
Метт Рогіш

17
+1, тому що моєму монітору все-таки було потрібно прибирання ;-)
RBerteig

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

1
Три слова: "Підшипники глушника хрому".
Allbite

53

Нічого не руйнує Земля, я визнаю, але я спіймав людей, які використовують StringBuffer для об'єднання рядків поза циклом на Java. Це було щось просте, як поворот

String msg = "Count = " + count + " of " + total + ".";

в

StringBuffer sb = new StringBuffer("Count = ");
sb.append(count);
sb.append(" of ");
sb.append(total);
sb.append(".");
String msg = sb.toString();

Раніше була звичайна практика використовувати техніку в циклі, оскільки це було помірно швидше. Справа в тому, що StringBuffer синхронізований, тому насправді є додаткові накладні витрати, якщо ви об'єднуєте лише кілька рядків. (Не кажучи вже про те, що різниця абсолютно тривіальна в цій шкалі.) Ще два моменти щодо цієї практики:

  1. StringBuilder не синхронізований, тому слід віддавати перевагу над StringBuffer у випадках, коли ваш код не може бути викликаний з декількох потоків.
  2. Сучасні компілятори Java перетворять читабельне об'єднання рядків у оптимізований байт-код для вас, коли це все одно підходить.

3
По-перше: чому б ви не використовували принаймні Java 5? Друге: Так, можна. Як так, що у першому прикладі ви можете порахувати до 5, а не у другому? У ній використовуються ті самі рядкові рядки, що й перші. Напишіть читабельний код і дозвольте компілятору вирішити, коли використовувати StringBuffer за кадром.
Білл Ящірка

4
@ MetroidFan2002: Лінійні рядки в другому прикладі теж є об'єктами. Як я вже сказав у відповіді, відмінності тривіальні в цій шкалі.
Білл Ящірка

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

3
@Eric: String msg = "Count =" + count + "of" + total + "." часто компілюється в Java до String msg = new StringBuffer (). append ("Count"). append (count) .append ("of") .append (total) .append ("."). toString (); ... саме це і робить другий приклад.
Грант Вагнер

3
Містер Вагнер, річ у тому, що ВИ повинні дивитися на всі ці виклики методів, а не на компілятор. Ви повинні їх написати та зрозуміти пізніше. У будь-якому випадку компілятор робить те саме. Тож читабельність у цьому випадку важливіша.
ypnos

47

Я колись бачив базу даних MSSQL, яка використовувала таблицю "Корінь". Коренева таблиця мала чотири стовпці: GUID (унікальний ідентифікатор), ID (int), LastModDate (дата) та CreateDate (дата). Усі таблиці в базі даних були зовнішнім ключем до таблиці Root. Щоразу, коли в будь-якій таблиці в db створено новий рядок , вам доведеться використовувати пару збережених процедур, щоб вставити запис у таблицю Root, перш ніж ви могли дістатися до фактичної таблиці, про яку ви піклувались (а не в базі даних, яка виконує цю роботу для ви з кількома тригерами прості тригери).

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

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

О, а схема бази даних виглядала як мутантний павук з пекла.


42

Як щодо POBI - песимізація очевидно з наміром?

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

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

До одного випуску він помістив багато нових затримок (200) функцій (це був Delphi) у новий код. Минуло лише 20 хвилин після виходу наживо, і йому було наказано з’явитися в офіс генерального директора, щоб особисто винести свої прострочені образи.

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

Природно, зараз мій колега відпочив на робочому столі один-два дні, вдосконалював свої навички націлювання на Quake - тоді на другий-третій день він видалив затримки дзвінків, відновив і випустив "аварійний патч", про який він поширив слово що він витратив 2 дні і 1 ніч, щоб виправити дірочки.

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

Це було справжнє POBI.

Але це також є свого роду оптимізацією соціальних процесів, тому це на 100% нормально.

Я думаю.


10
Я пам'ятаю, як хтось писав про додаток для обробки даних, який продавався на різних рівнях, де "Lite" міг розчавити лише кілька наборів даних в секунду, "супердупер" тисячі версій. Єдина різниця вихідного коду - це сон (N).
peterchen

1
Блискуче! Я рекомендував би цей стандарт у такій ситуації. На початку розробки виділіть великий шматок пам’яті та додайте кілька дзвінків у режим сну, а коли вам потрібно буде виконати певну продуктивність, просто поцікавте їх. Це називається чудотворцем;)
RCIX

На жаль, виправлення "Sleeps" на NOP - це легко, так що легку версію можна зламати дуже легко. Цей резерв "оптимізації" може зажадати виконуваного пакера, щоб зробити налагодження та виправлення більш складним.
TheBlastOne

32

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


8
Це "незалежність" в тому сенсі, що ви так далеко вище бази даних, ви забули, що це за дані? Зайве абстрагування над базами даних "щоб уникнути міграційних болів" є домашнім вихованцем; тобі це не знадобиться.
Роб

8
Досить. Архітектура космонавтів на роботі. Я будую веб-додатки з часу існування Інтернету, і за весь цей час я ніколи фактично не переходив з однієї платформи db на іншу.
chris

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

6
гарпу, це інша ситуація - це вимога в такому випадку. Я говорю про те, коли це не вимога, але АА вирішує, що це "може бути" в якийсь момент.
chris

3
@All: Незалежність БД може коштувати вам, так, але наш продукт працює у середовищах, де постачальник баз даних обирається ставками, і нам, в основному, потрібно пограти. Деякі розробники не отримують розкіш у вертикально інтегрованому стеку програмного забезпечення, і це потрібно зробити, незважаючи на це.
Кріс Р

31
var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();

Найкраще використовувати StringBuilder, який я коли-небудь бачив.


9
Говоріть про "незрозуміло щодо концепції"! Оце Так!
Едді

3
Класно. "Моя ведуча говорить, що я повинен використовувати клас StringBuilder, якщо хочу об'єднати рядки. Це те, що я роблю. Отже, що не так?" Lol ...
TheBlastOne

26

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


25
АЛЕ в Java String.Split використовує регулярний вираз!
Френк Крюгер

Я не бачу, наскільки Regex може бути таким же швидким, як внутрішній струнний розкол.
Андрій Ронеа

2
Але навмисне пошук регексу, який використовується для розбиття рядків і заміни його на «просту» функцію розділення, звучить як ідеальний приклад песимізації. Бібліотеки Regex досить швидко.
David Crawshaw

5
@David Crawshaw: Зниження можливостей мікрооптимізації витрачає людський час; буд при написанні коду використовуйте найменш складне достатнє рішення.
Пісквор вийшов з будівлі

6
-1: Якщо ви звикли до регулярних виразів, дуже природно писати це замість того, щоб звикнути до маніпуляторів 1001 мов-внутрішніх рядків.
KillianDS

26

Я дуже пізно знаю цю тему, але нещодавно я це бачив:

bool isFinished = GetIsFinished();

switch (isFinished)
{
    case true:
        DoFinish();
        break;

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}

Знаєте, про всяк випадок, коли булева матиме додаткові значення ...


22
True, False FileNotFound Ofcourse
IKKE

Ей, у вас завжди має бути типовий / інший випадок / тощо. Що трапляється, коли якась яскрава людина змінює булів на перерахунок, щоб відобразити такий інший статус, тоді наступна людина додає до перерахунку і забуває змінити процедуру? За замовчуванням, коли не потрібно витрачати часу на виконання та дуже мало часу на розробку. Відстеження випадково введеної логічної помилки, яка виникає під час виконання ... Це коштує часу, грошей та репутації. Шов в часі економить дев’ять.
Ооранг

1
@Oorang ... навіщо б ти його як перемикач все одно? Це булеве значення - якщо все ще потрібно.
Дамовіса

@Damovisa facepalm справа ... дуже добре тоді :) Пропустив це :)
Ооранг,

2
Це було Nullable <Boolean> ... :)
Джордж Чахідзе

25

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

Песимізм полягає в тому, що розробник думав, що повторні дзвінки до веб-сервісу будуть надто повільними, щоб робити повторні запити SQL. То що він зробив? Подія запуску програми читається у всій базі даних і перетворює все це на об'єкти в пам'яті, що зберігаються на невизначений термін, поки пул додатків не буде перероблений. Цей код був настільки повільним, що знадобиться 15 хвилин, щоб завантажити менше 2000 співробітників. Якщо ви ненавмисно переробили пул додатків протягом дня, це може зайняти 30 хвилин або більше, тому що кожен запит веб-служби запускатиме кілька одночасних перезавантажень. З цієї причини нові робочі місця не з’являться в базі даних у перший день, коли був створений їхній обліковий запис, і тому вони не зможуть отримати доступ до більшості внутрішніх додатків у перші кілька днів, подвівши пальці.

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


28
Управління в їх кращих випадках - "Ні, не давайте вкладати одноразові 80 програмістських годин на виправлення цього додатка, це занадто дорого. Давайте просто збережемо його, щоб його помилки могли витрачати 200+ годин користувачів на місяць, плюс 10 годин програміста на місяць за "технічне обслуговування". " AAAAAAAAAUGH !!!
Пісквор вийшов із будівлі

25

Здається, ніхто не згадував про сортування, тому я і буду.

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


2
Я робив це сам один раз, коли визначив, що зазвичай n = 2. Пізніші вдосконалення продукту втратили мій привід, і код був замінений PDQ.
Марк Викуп

2
Так, але приємно написати що-небудь інше, засноване на алгоритмі ;)
UpTheCreek

20

Я колись працював над додатком, який був таким кодом:

 1 tuple *FindTuple( DataSet *set, int target ) {
 2     tuple *found = null;
 3     tuple *curr = GetFirstTupleOfSet(set);
 4     while (curr) {
 5         if (curr->id == target)
 6             found = curr;
 7         curr = GetNextTuple(curr);
 8     }
 9     return found;
10 }

Просто видаліть found, повернувшись nullнаприкінці та змінивши шостий рядок на:

            return curr;

Удвічі ефективність програми.


1
Я працював одного разу в компанії, де вказівки щодо кодування вимагали "лише одного повернення в кінці" (для обслуговування). І справді, деякі коси коду, як ваш, тому що вони не думають (очевидними рішеннями були більшість випадків, використовуючи goto до виходу
PRO

12
Зворотний курс тут викликає помітно іншу поведінку. Після повернення curr ви отримуєте ПЕРШИЙ збіг, де як вставлений код повертає ОСТАННУЮ відповідність.
SoapBox

2
@SoapBox: Ви маєте рацію. @Dour High Arch: Підвищення продуктивності не має нічого спільного з єдиним правилом повернення, оскільки Флоло сказав, що переслідування умови циклу на (curr &&! Знайдено) матиме той же ефект. ГОТО до виходу проц - жахливе і перемагає мету єдиного керівництва щодо повернення.
Акусете

2
Гарні коментарі всім. У цьому випадку повинен був бути лише один кортеж із кожним посвідченням особи.
Dour High Arch

7
Але це не "песимізація", чи не так? Це просто оптимізація, яка чекає цього.
Тім Лонг

20

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

public static String COMMA_DELIMINATOR=",";
public static String COMMA_SPACE_DELIMINATOR=", ";
public static String COLIN_DELIMINATOR=":";

Кожен із них використовувався кілька разів у решті програми для різних цілей. COMMA_DELIMINATOR заповнив код із понад 200 використанням у 8 різних пакетах.


Принаймні щось подібне легко знайти / замінити з джерела - все-таки мої симпатії.
Ерік Форбс

12
Також - роздільник? Я подумав, що це написано "роздільник". Розмежувач звучить як поганий фільм середини 90-х, який якось отримав 3 продовження ...........
Ерік Форбс,

53
Віддільник III: Повстання Коми
Роб

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

2
Визначити та замінити це не так просто. Оскільки кожен з них використовується для різних цілей. Будь-який хороший програміст зробив би принаймні щось подібне: COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ... тощо
KitsuneYMG

19

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

Не використовувати функції СУБД з причин "портативності", оскільки "ми можемо захотіти пізніше перейти до іншого постачальника".

Читай мої губи. Для будь-якої домашньої роботи: НЕ БУДЕ ДАЄТЬСЯ!


9
Це трапляється. MySQL -> postgresql, тому ми нічого не втратили .
Томас

Або postgres / postgis -> sqlite /paceite ... То був біль у дупі ...
Філіп

це трапляється в тестах JUnit
качанів

17

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

int some_method(int input1, int input2) {
    int x;
    if (input1 == -1) {
        return 0;
    }
    if (input1 == input2) {
        return input1;
    }
    ... a long expression here ...
    return x;
}

в це:

int some_method() {
    return (input == -1) ? 0 : (input1 == input2) ? input 1 :
           ... a long expression ...
           ... a long expression ...
           ... a long expression ...
}

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

Потім, після цього, він чомусь вирішив, що ptr->structElementце занадто нечитабельно, тому він почав змінювати все це (*ptr).structElementна теорію, що це також читабельніше і швидше.

Перетворення читабельного коду в нечитабельний код для щонайменше на 1% покращення, а іноді й фактично повільнішого коду.


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

2
@Michael: Я б не робив, якщо б не було вимірювань, які вказували на те, що це швидше , а не просто коротше .
dimimcha

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

2
Проблема тут полягає у прийнятті цілої функції та заміні її одним оператором, поверненням, тим самим замінивши всю логіку всієї функції вкладеними терміналами. Якби ти це бачив, ти зрозумів би. Це не релігійна річ "Я ненавиджу побічних операторів". Я не говорю про те, щоб взяти сингл ifу функції та замінити його на трійку. Це добре, а часто і читабельніше. Я говорю про заміну цілого методу рядка 30+ одним оператором повернення та вкладеними терміналами. Ніхто не думав, що новий код читається, але один розробник вважав, що це швидше.
Едді

15

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

Коли я розкопався, я виявив, що оригінальний програміст прагнув прискорити роботу, паралелізуючи аналіз - запустивши нову нитку для кожного додаткового джерела даних. Однак він допустив помилку в тому, що всі потоки вимагали спільного ресурсу, на якому вони зайшли в глухий кут. Звичайно, всі переваги одночасності зникли. Більше того, він розбив більшість систем, щоб запустити 100+ потоків лише для того, щоб усі, крім однієї, були заблоковані. Моя машина "BEfy Dev" стала винятком у тому, що вона спрацьовувала за допомогою набору даних 150 джерел приблизно за 6 годин.

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


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

14

Я думаю, я міг запропонувати цей дорогоцінний камінь:

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1, root = 0;
    #define ISQRT_INNER(shift) \
    { \
        if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
        { \
            root += 1 << shift; \
            value -= tmp; \
        } \
    }

    // Find out how many bytes our value uses
    // so we don't do any uneeded work.
    if (value & 0xffff0000)
    {
        if ((value & 0xff000000) == 0)
            tmp = 3;
        else
            tmp = 4;
    }
    else if (value & 0x0000ff00)
        tmp = 2;

    switch (tmp)
    {
        case 4:
            ISQRT_INNER(15);
            ISQRT_INNER(14);
            ISQRT_INNER(13);
            ISQRT_INNER(12);
        case 3:
            ISQRT_INNER(11);
            ISQRT_INNER(10);
            ISQRT_INNER( 9);
            ISQRT_INNER( 8);
        case 2:
            ISQRT_INNER( 7);
            ISQRT_INNER( 6);
            ISQRT_INNER( 5);
            ISQRT_INNER( 4);
        case 1:
            ISQRT_INNER( 3);
            ISQRT_INNER( 2);
            ISQRT_INNER( 1);
            ISQRT_INNER( 0);
    }
#undef ISQRT_INNER
    return root;
}

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

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1, root = 0;
    #define ISQRT_INNER(shift) \
    { \
        if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
        { \
            root += 1 << shift; \
            value -= tmp; \
        } \
    }

    ISQRT_INNER (15);
    ISQRT_INNER (14);
    ISQRT_INNER (13);
    ISQRT_INNER (12);
    ISQRT_INNER (11);
    ISQRT_INNER (10);
    ISQRT_INNER ( 9);
    ISQRT_INNER ( 8);
    ISQRT_INNER ( 7);
    ISQRT_INNER ( 6);
    ISQRT_INNER ( 5);
    ISQRT_INNER ( 4);
    ISQRT_INNER ( 3);
    ISQRT_INNER ( 2);
    ISQRT_INNER ( 1);
    ISQRT_INNER ( 0);

#undef ISQRT_INNER
    return root;
}

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

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

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1 << 30, root = 0;

    while (tmp != 0)
    {
        if (value >= root + tmp) {
            value -= root + tmp;
            root += tmp << 1;
        }
        root >>= 1;
        tmp >>= 2;
    }

    return root;
}

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


Я припускаю, що isqrt()обчислює floor(sqrt()), але чому цей код працює?
Пабло Н

11

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

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


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

Я ніколи не робив висновку, що це завжди погана ідея. Якщо ви не скажете Frans Bouma чи подібне, я сумніваюся, що ORM / DAL є основною функцією бізнесу. Написати власний еквівалент - винайдення (квадратного) колеса, як правило, викликано синдромом НІГ, надзвичайно недоцільно.
Гордон Хартлі

@Kibbee - я згоден. Краще скрутити своє і зрозуміти це, ніж використовувати сторонні залежності. Коли він зламається (і це станеться), ви принаймні ви зможете це виправити. У минулому я виявляв помилки в режимі Hibernate та Apache Commons, які абсолютно вбивали продуктивність нашого додатка.
CodingWithSpike

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

3
Насправді, з огляду на деякі коментарі, наведені вище, ще одна перспектива: ще одна песимізація - це спробувати змусити ОРМ зробити абсолютно все. Його часто корисно для 95% + випадків. Для останніх 5% набагато простіше відмовитись від створеного вручну коду стійкості / прямих викликів збережених процедур тощо для виконання, простоти або обох.
Гордон Хартлі

10

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


8

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


8

Перед кожним операцією JavaScript перевіряйте, чи існує об'єкт, над яким ви працюєте.

if (myObj) { //or its evil cousin, if (myObj != null) {
    label.text = myObj.value; 
    // we know label exists because it has already been 
    // checked in a big if block somewhere at the top
}

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

Я погоджуюся, що Object expectedпомилки дратують, але це не найкраще рішення для цього.


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

7

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


3
Дякую, мені нудно від цих кульгавих акронімів "екстремального програмування" та того, як люди використовують їх для підтримки ледачих, контрпродуктивних практик.
JAL

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

@peterchen - Ви говорите, що дослідження показують, що для запису коду для багаторазового використання як одноразового коду потрібно три рази більше, або що вони показують, що для перетворення одноразового коду в код для багаторазового використання потрібно три рази, ніж для написання код багаторазового використання в першу чергу?
Jeff Sternal

@jeff: IIRC вони порівняли деяку міру складності (що б ви не думали про них) вбудованих фрагментів, які перейшли до окремих методів. Складність збільшується через додаткові підтримувані випадки, перевірку параметрів тощо (що змушує мене припускати, що методів було досить мало). Дозвольте спробувати викопати довідку.
peterchen

6

Не зовсім передчасна оптимізація - але, безумовно, помилково - про це читали на веб-сайті BBC, із статті, що обговорювала Windows 7.

Г-н Курран сказав, що команда Microsoft Windows розглядала всі аспекти операційної системи для вдосконалення. "Нам вдалося поголити 400 мілісекунд часу відключення, трохи обрізаючи музику відключення файлів WAV.

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


Напевно, ви побачите, що інші проблеми не так просто пояснити непрограмістам на веб-сайті BBC.
Том Лейс

Тепер це кут, який я не враховував - можливо, я починаю втрачати свій цинізм :-)
belugabob

Ці 400 мс - це 400 мс потужності. Напевно, незначне, але, можливо, воно з часом накопичується. Все-таки не те, про що я б хвилювався.
ZachS

1
Всього я втратив багато годин, чекаючи, поки вимкнеться VM з XP, щоб перейти до наступного. Я дуже вдячний за швидше закриття.
Джеймс

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

6

Хтось із мого відділу колись написав клас струн. Інтерфейс як CString, але без залежності Windows.

Одна з «оптимізацій», яку вони зробили, - це не виділяти більше пам'яті, ніж потрібно. Мабуть, не усвідомлюючи, що класи класів причин std::stringвиділяють надлишки пам'яті, щоб послідовність +=операцій могла виконуватись в O (n) час.

Натомість кожен окремий +=виклик примушував перерозподіл, який перетворював повторні додатки в алгоритм Шлемієля Художника О (n²) .


5

Екс-співробітник шахти (власне мила ) був призначений створити новий модуль для нашої Java ERP, який повинен був збирати та аналізувати дані клієнтів (роздрібна промисловість). Він вирішив розділити КОЖНЕ поле календаря / дату на його компоненти (секунди, хвилини, години, день, місяць, рік, день тижня, біместер, триместр (!)), Оскільки "як інакше я б запитав" кожен понеділок "?"


3
Це не передчасна оптимізація, він вважав, що потрібно це зробити для коректності
Піролістичний

Звичайно, він подумав, що йому це потрібно, але оскільки більшість СУБД мають якусь функцію DAYOFWEEK (часова мітка), робити це безладдя
наперед

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

Ну, я не знаю, чи було так багато рядків, але, безумовно, це не пояснення, які давали :)
Жоріл

3

Нікого не ображаючи, але я просто оцінив завдання (java), яке мало це

import java.lang.*;

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

24
Чи буду я єдиним, хто зазначив іронію викладача, який викликає WTF, на код студента, що він / вона відповідає за навчання правильному програмуванню?
JohnFx

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

1
Дякую всім за те, що ви сказали мені очевидне. Це було завдання з обчислювальної біології, і я його не рахував і навіть не згадував.
Переліт

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