Какую самую нелепую пессимизацию вы видели? [закрыто]

145

Все мы знаем, что преждевременная оптимизация является корнем всего зла, потому что оно приводит к нечитаемому / не поддерживаемому коду. Еще хуже пессимизация, когда кто-то реализует «оптимизацию», потому что он думает, что она будет быстрее, но в конечном итоге она будет медленнее, а также будет содержать ошибки, не поддерживаться и т. Д. Какой самый нелепый пример этого вы видели ?

dsimcha
источник
21
«Пессимизация» - прекрасное слово.
MQP
На всякий случай, если вы не знали, они говорили о вашей теме здесь на последнем подкасте.
mmcdole
Файл жаргона: catb.org/jargon/html/P/pessimal.html , catb.org/jargon/html/P/pessimizing-compiler.html
Бени Чернявский-Паскин

Ответы:

81

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

Нашей новой средой был 32-битный Sparc Solaris.

Один из парней пошел и заменил все целые числа на шорты, чтобы ускорить наш код, поскольку захват 16 битов из оперативной памяти был быстрее, чем захват 32 битов.

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

Марк Харрисон
источник
16
Эй, где ты изучил свою математику? 2 инструкции с 1 доступом к кэш / памяти явно быстрее, чем 1 инструкция с 1 доступом к кэш / памяти!
Razor Storm
2
@RazorStorm На более поздних машинах, где пропускная способность и кэш более ценны, было бы обратное. Битовая маска / смещение дешевая, но вы хотите разместить как можно больше в кэше, а также минимизировать пропускную способность.
Джед
206

Я думаю, что фраза «преждевременная оптимизация - корень всего зла» - это путь, путь использованный. Для многих проектов это стало поводом не принимать во внимание производительность до поздней стадии проекта.

Эта фраза часто является опорой для людей, чтобы избежать работы. Я вижу эту фразу, когда люди действительно должны сказать: «Ну и дела, мы действительно не думали об этом заранее, и у нас нет времени, чтобы разобраться с этим сейчас».

Я видел гораздо больше «нелепых» примеров глупых проблем с производительностью, чем примеров проблем, вызванных «пессимизацией»

  • Чтение одного и того же ключа реестра тысячи (или десятки тысяч) раз при запуске программы.
  • Загрузка одной и той же DLL сотни или тысячи раз
  • Тратить мегабайты памяти, сохраняя полный путь к файлам без необходимости
  • Не организовывать структуры данных, чтобы они занимали больше памяти, чем им нужно
  • Определение размера всех строк, в которых хранятся имена файлов или пути к MAX_PATH
  • Бесплатный опрос для вещи, которая имеет события, обратные вызовы или другие механизмы уведомления

Я думаю, что лучше сказать следующее: «Оптимизация без измерения и понимания вовсе не является оптимизацией - это просто случайное изменение».

Хорошая производительность требует значительных затрат времени - чаще всего для разработки самой функции или компонента.

Foredecker
источник
46
«Преждевременный» является ключевым словом этой цитаты. Ваша перефразировка в «оптимизацию без измерения и понимания», кажется, не меняет смысла. Это именно то, что имел в виду Кнут.
Билл Ящерица
13
@ Foredecker: отлично. Слишком многие люди забывают контекст, который ставит , что цитата прочно против микро -оптимизации. Анализ проблемы для выбора правильного алгоритма до его реализации не является преждевременным, но слишком часто эта цитата подбрасывается, чтобы оправдать самое ленивое, самое неэффективное решение.
Shog9
5
Это действительно зависит от конкретного случая, есть больше случаев, когда преждевременная оптимизация становится проблемой, чем неадекватное планирование оптимизации становится проблемой
Марк Роджерс
12
-1: есть разница между «оптимизацией» и правильным дизайном. Для тех, кто не может сказать, хорошее эмпирическое правило заключается в том, что «оптимизация» делает код более трудным для чтения, но быстрее или эффективнее. Лучший дизайн сделает код более легким для чтения (или, по крайней мере, не хуже) и более эффективным.
TED
5
Если он чрезмерно используется, то население, задающее вопросы по SO, сильно взвешено по отношению к выбросам. : D
dkretz
114

Базы данных являются пессимизацией игровых площадок.

Избранное включает в себя:

  • Разделите таблицу на несколько (по диапазону дат, алфавитному диапазону и т. Д.), Потому что он «слишком большой».
  • Создайте архивную таблицу для удаленных записей, но продолжайте объединять ее с рабочей таблицей.
  • Дублировать целые базы данных по (подразделение / клиент / продукт / и т. Д.)
  • Не добавляйте столбцы в индекс, потому что он слишком велик.
  • Создайте много сводных таблиц, потому что пересчет из необработанных данных выполняется слишком медленно.
  • Создайте столбцы с подполями для экономии места.
  • Денормализовать в поля как массив.

Это с моей головы.

le dorfier
источник
Сопротивление необходимости индексировать просто больно думать.
Билл Ящерица
2
Да, я знаю человека, который работает в крупной нефтяной компании США, где почти все их таблицы имеют связанную архивную таблицу, и большинство запросов выбирают из представлений, объединяющих пары таблиц. Производительность, как и следовало ожидать!
Тони Эндрюс
Ха, я думаю, что в какой-то момент каждый администратор базы данных должен был пройти маршрут объединения архивных таблиц. Это всегда кажется таким разумным в то время.
Cruachan
3
Я добавляю: разделить базу данных на несколько разных баз данных (клиенты ac, клиенты df и т. Д.)
Gabriele D'Antona
Не могли бы вы рассказать о «денормализации в поля как массив»? Что вы имеете в виду здесь?
Барт ван Хейкелом
87

Я думаю, что нет абсолютного правила: некоторые вещи лучше всего оптимизировать заранее, а некоторые нет.

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

С другой стороны, наши разработчики программного обеспечения решили, что распакованные данные должны быть отформатированы в очень читаемый XML-документ и сохранены в нашей базе данных как таковой (в отличие от хранения каждого поля в соответствующем столбце). Их идея заключалась в том, что «XML - это будущее», «дисковое пространство дешевое» и «процессор дешевый», поэтому не нужно было ничего оптимизировать. В результате наши 16-байтовые пакеты были превращены в документы размером 2 КБ, хранящиеся в одном столбце, и даже для простых запросов нам пришлось загружать мегабайты документов XML в память! Мы получали более 50 пакетов в секунду, поэтому вы можете себе представить, насколько ужасной стала производительность (кстати, компания обанкротилась).

Итак, еще раз, нет абсолютного правила. Да, иногда слишком ранняя оптимизация является ошибкой. Но иногда девиз «процессор / дисковое пространство / память дешев» - это настоящий корень всего зла.

MiniQuark
источник
37
Я согласен, что «процессор / дисковое пространство / память дешевы» - это настоящий корень всего зла. +1
ксуралта
5
Я слышал, что XML тоже болтается. Еще одна танковая компания.
n8wrl
19
@ksuralta: «Процессор / дисковое пространство / память дешевы» - это удобное оправдание, чтобы избежать мыслей. Избегание мысли - это мнимый корень зла.
Писквор покинул здание
Эта XML-обработка произошла и на моем рабочем месте, после чего последовала JSON-обработка. Все, чтобы избежать "трудоемкого" проектирования реляционных баз данных.
Tanz87
75

О, Господи, я думаю, что видел их всех. Чаще всего это попытка исправить проблемы с производительностью кем-то, кто слишком ленив, чтобы выяснить причину этих проблем с производительностью или даже выяснить, действительно ли существует проблема с производительностью. Во многих из этих случаев мне интересно, не тот ли это случай, когда человек хочет попробовать какую-то особую технологию и отчаянно ищет гвоздь, который подходит для их нового блестящего молотка.

Вот недавний пример:

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

Я: Почему вы рассматриваете это? Какую проблему вы пытаетесь решить?

Он: Таблица X слишком широка, мы разбиваем ее по соображениям производительности.

Я: Что заставляет вас думать, что оно слишком широкое?

Он: Консультант сказал, что в одной таблице слишком много столбцов.

Я: И это влияет на производительность?

Ему: Да, пользователи сообщают о периодических замедлениях в модуле XYZ приложения.

Меня: Откуда вы знаете, что ширина таблицы является источником проблемы?

Ему: Это таблица ключей, используемая модулем XYZ, и она похожа на 200 столбцов. Это должно быть проблемой.

Я (Объяснение): Но модуль XYZ, в частности, использует большинство столбцов в этой таблице, и столбцы, которые он использует, непредсказуемы, поскольку пользователь настраивает приложение для отображения данных, которые он хочет отобразить из этой таблицы. Вполне вероятно, что в 95% случаев мы все равно будем объединять все столы вместе, что ухудшит производительность.

Ему: Консультант сказал, что он слишком широкий, и нам нужно его изменить.

Меня: Кто этот консультант? Я не знал, что мы наняли консультанта, и они вообще не разговаривали с командой разработчиков.

Ему: Ну, мы еще не наняли их. Это часть предложения, которое они предложили, но они настаивали на том, что нам нужно реорганизовать эту базу данных.

Меня: Ага. Поэтому консультант, который продает услуги по перепроектированию базы данных, считает, что нам нужно перепроектировать базу данных ....

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

  1. Отсутствуют индексы по нескольким ключевым столбцам.
  2. Несколько мошеннических аналитиков данных, которые периодически блокировали таблицы ключей (включая «слишком широкую»), обращаясь непосредственно к производственной базе данных с помощью MSAccess.

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

оборота JohnFx
источник
Aaag MSAccess для прод. Мы написали процедуру для сброса всех подключений доступа каждые несколько минут, и в конце концов мы получили сообщение, что это плохо.
Nat
1
У нас была похожая работа, но она вышла из употребления. Чтобы быть справедливым, доступ не является проблемой, он просто позволяет неофитам создавать / выполнять неработающие запросы.
JohnFx
В нашей компании мы зависим от устаревших специальных соединений Access с производственной БД. Нет ничего лучше, чем несколько случайных SQL-операторов, которые забывают предложение WHERE и блокируют основные таблицы!
HardCode
35
«Я слышал, что у
mauve
Это могло бы быть и хуже. Редактор запросов Excel блокирует всю базу данных при ее использовании. Когда я не знал об этом, я оставил один его экземпляр открытым для большей части дня, пока работал над чем-то другим. Хуже всего то, что MS SQL Server не сообщал правильное имя пользователя / машину, которая выполняла блокировку. Несколько часов спустя я понял, что я был причиной блокировки из-за того, что заблокированные таблицы были частью представления, которое я запрашивал и проверял все остальное в первую очередь.
Эстебан Кубер
58

Я видел людей, использующих alphadrive-7 для полной инкубации CHX-LT. Это необычная практика. Более распространенной практикой является инициализация ZT-преобразователя, чтобы уменьшить буферизацию (из-за большей устойчивости к перегрузке) и создать байтовую символику в стиле Java.

Абсолютно пессимистично!

Zalaga
источник
10
возможно они пытались закапать конденсатор потока
Mikeage
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 + "из" + total + "."; в Java часто компилируется в String msg = new StringBuffer (). append ("Count"). append (count) .append ("of") .append (total) .append ("."). toString (); ... это именно то, что делает второй пример.
Грант Вагнер
3
Мистер Вагнер, дело в том, что вы должны смотреть на все эти вызовы методов, а не на компилятор. Вы должны написать их и понять их позже. Компилятор делает то же самое в любом случае. Так что читаемость важнее в этом случае.
ypnos
47

Однажды я увидел базу данных MSSQL, в которой использовалась таблица «Root». Корневая таблица имела четыре столбца: GUID (уникальный идентификатор), ID (int), LastModDate (datetime) и CreateDate (datetime). Все таблицы в базе данных были Внешним Ключом к Корневой таблице. Всякий раз, когда новая строка была создана в любом таблице в БД , вам приходилось использовать пару хранимых процедур для вставки записи в корневую таблицу, прежде чем вы могли бы перейти к фактической таблице, которая вам нужна (вместо того, чтобы база данных выполняла работу для Вы с несколькими триггерами простых триггеров).

Это создало беспорядок бесполезных подслушиваний и головных болей, требовало от всего, что написано на нем, использовать sprocs (и исключало мои надежды представить LINQ для компании. Это было возможно, но просто не стоило головной боли), и завершить это не стало ' даже не выполнить то, что предполагалось.

Разработчик, выбравший этот путь, защитил его, предполагая, что это сэкономило тонны пространства, потому что мы не использовали Guids для самих таблиц (но ... не генерируется ли GUID в корневой таблице для каждой строки, которую мы создаем?) каким-то образом улучшил производительность и сделал «легким» аудит изменений в базе данных.

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

Dusda
источник
42

Как насчет POBI - пессимизация, очевидно, по намерению?

Моя коллега в 90-х устала от того, что генеральный директор пинал ее в задницу только потому, что генеральный директор провел первый день каждого выпуска программного обеспечения ERP (пользовательского) с поиском проблем с производительностью в новых функциях. Даже если новые функциональные возможности сокращали гигабайты и делали невозможное возможным, он всегда находил какие-то детали или даже, казалось бы, серьезную проблему, чтобы скулить. Он считал, что знает много о программировании и получил свои удары ногами программиста.

Из-за некомпетентного характера критики (он был генеральным директором, а не ИТ-специалистом), моему коллеге так и не удалось сделать это правильно. Если у вас нет проблемы с производительностью, вы не можете ее устранить ...

Вплоть до одного выпуска он поместил много вызовов функции Delay (200) (это был Delphi) в новый код. Прошло всего 20 минут после начала работы, и ему было приказано явиться в офис генерального директора, чтобы лично принести ему запоздалые оскорбления.

Единственным необычным моментом было то, что мои коллеги молчали, когда он возвращался, улыбался, шутил, выходил на BigMac или два, пока он обычно пинал столы, разглагольствовал о генеральном директоре и компании, а остаток дня провел до смерти ,

Естественно, мой коллега теперь отдыхал в течение одного или двух дней за его столом, улучшая свои навыки прицеливания в Quake - затем на второй или третий день он удалил вызовы задержки, восстановил и выпустил «экстренный патч», о котором он распространял слово что он потратил 2 дня и 1 ночь, чтобы исправить дыры в производительности.

Это был первый (и единственный) раз, когда злой генеральный директор сказал "отличная работа!" Для него. Это все, что имеет значение, верно?

Это было настоящее ПОБИ.

Но это также своего рода оптимизация социального процесса, так что все в порядке.

Думаю.

TheBlastOne
источник
10
Я помню, как кто-то писал о приложении для обработки данных, которое было продано на разных уровнях, где «Lite» мог обрабатывать только несколько наборов данных в секунду, а «супер» - тысячи версий. Единственная разница в исходном коде - Sleep (N).
peterchen
1
Brilliant! я бы порекомендовал этот стандарт в такой ситуации. В начале разработки выделите большой кусок памяти и добавьте некоторые спящие вызовы, а когда вам понадобится добиться некоторой производительности, просто объедините их. Это называется быть чудотворцем;)
RCIX
К сожалению, исправление Sleeps to NOPs очень просто, поэтому облегченную версию можно легко взломать. Этот резерв «оптимизации» может потребовать исполняемого упаковщика, чтобы усложнить отладку и исправление.
TheBlastOne
32

«База данных независимости». Это означало отсутствие хранимых процедур, триггеров и т. Д. - даже никаких внешних ключей.

Крис
источник
8
Является ли это «независимостью» в том смысле, что вы настолько далеко над базой данных, что забыли, что это за данные? Излишне абстрагироваться от баз данных «чтобы избежать трудностей миграции» - любимая мозоль; тебе это не понадобится.
Роб
8
Довольно много. Архитектура космонавтов за работой. Я создавал веб-приложения с тех пор, как появилась сеть, и за все это время я никогда не переходил с одной платформы БД на другую.
Крис
5
Я уверен, что это происходит, но достаточно редко, что вы идиот, если вы разрабатываете свою архитектуру с учетом этой возможности.
Крис
6
Гарпо, это другая ситуация - это требование в этом случае. Я говорю о том, когда это не является обязательным требованием, но АА решает, что это "может быть" в какой-то момент.
Крис
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

Cherian
источник
25
НО в Java String.Split использует регулярное выражение!
Фрэнк Крюгер,
Я не понимаю, как Regex может быть таким же быстрым, как внутреннее разбиение строки.
Андрей Ринея
2
Но преднамеренное выискивание регулярного выражения, используемого для разделения строк, и замена его «простой» функцией разделения звучит как прекрасный пример пессимизации. Библиотеки Regex достаточно быстрые.
Дэвид Крошау
5
@David Crawshaw: выслеживание возможностей микрооптимизации тратит впустую человеческое время; При написании кода используйте наименее сложное и достаточное решение.
Писквор покинул здание
6
-1: если вы привыкли к регулярным выражениям, то это вполне естественно написать вместо того, чтобы привыкнуть к внутренним языковым манипуляторам 1001.
KillianDS
26

Очень поздно к этой теме я знаю, но я видел это недавно:

bool isFinished = GetIsFinished();

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

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}

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

Damovisa
источник
22
Правда, Ложь, конечно, FileNotFound
Ikke
Эй, вы всегда должны иметь по умолчанию / case иначе / и т.д. Что происходит, когда какой-то яркий человек меняет это логическое значение на перечисление, чтобы отразить другой статус, тогда следующий человек добавляет перечисление и забывает изменить процедуру? Наличие значения по умолчанию, когда вам не нужно тратить время на выполнение и очень мало времени на разработку. Отслеживание случайно введенной логической ошибки, которая происходит во время выполнения ... Это стоит времени, денег и репутации. Стежок, сделанный вовремя, стоит девяти.
Oorang
1
@Oorang ... зачем тебе это как переключатель? Это логическое значение - все, что требуется, это if / else.
Дамовиза
@Damovisa Facepalm право ... очень хорошо тогда :) Пропущенный что :)
Oorang
2
Это был Nullable <Boolean> ... :)
Георгий Чахидзе
25

Худший пример, который я могу вспомнить, - это внутренняя база данных в моей компании, содержащая информацию обо всех сотрудниках. Он получает еженедельное обновление от HR и имеет веб-сервис ASP.NET на вершине. Многие другие приложения используют веб-сервис для заполнения таких вещей, как поля поиска / раскрывающегося списка.

Пессимизм заключается в том, что разработчик считал, что повторные вызовы веб-службы будут слишком медленными для выполнения повторных запросов SQL. И что он сделал? Событие запуска приложения считывает всю базу данных и преобразует все это в объекты в памяти, которые хранятся неопределенно долго, пока пул приложений не будет перезагружен. Этот код был настолько медленным, что его загрузка заняла бы менее 15 минут у менее чем 2000 сотрудников. Если вы случайно переработали пул приложений в течение дня, это может занять 30 минут или более, поскольку каждый запрос веб-службы будет запускать несколько одновременных перезагрузок. По этой причине новые сотрудники не будут появляться в базе данных в первый день, когда была создана их учетная запись, и, следовательно, не смогут получить доступ к большинству внутренних приложений в первые пару дней, трясясь пальцами.

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

spoulson
источник
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;

Удвоил производительность приложения.

Dour High Arch
источник
1
Однажды я работал в компании, где правила кодирования требовали «только одного возврата в конце» (для поддержки). И действительно, некоторые выкрикивают код, подобный вашему, потому что они не думают (очевидные решения в большинстве случаев заключались в использовании goto для выхода из proc или изменения условий выхода из циклов)
flolo
12
Возврат curr здесь производит заметно другое поведение. Когда вы возвращаете curr, вы в конечном итоге получаете первое совпадение, где в качестве вставленного вами кода возвращается последнее совпадение.
SoapBox
2
@ SoapBox: Вы правы. @Dour High Arch: Увеличение производительности не имело ничего общего с правилом одиночного возврата, поскольку flolo сказал, что выполнение условия цикла для (curr &&! Found) будет иметь тот же эффект. GOTO к выходу proc является ужасным и побеждает цель единого правила возврата.
Akusete
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 различных пакетах.

KitsuneYMG
источник
По крайней мере, что-то подобное легко найти / заменить из источника - тем не менее, мои симпатии.
Эрик Форбс
12
Также - Делиминатор? Я думал, что это было написано «разделитель». Deliminator звучит как плохой фильм середины 90-х, который каким-то образом получил 3 продолжения ...........
Эрик Форбс
53
Разделитель III: Восстание запятых
Роб
33
С другой стороны, я рад видеть правильное разграничение Колинса. Каждый программист, достойный его соли, знает, что если есть что-то, что вы абсолютно должны отделить должным образом, это чертов Колинс.
Роб
2
Это не так просто сделать правильный поиск и замену. Поскольку каждый из них используется для разных целей. Любой хороший программист, по крайней мере, сделал бы что-то вроде этого: COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ... и т. Д.
KitsuneYMG
19

Большое все время номер один, с которым я сталкиваюсь снова и снова в собственных программах:

Не использовать функции СУБД по причинам «переносимости», потому что «мы можем захотеть переключиться на другого поставщика позже».

Читай по губам. Для любой внутренней работы: ЭТОГО НЕ БУДЕТ!

Питер Стуер
источник
9
Это случается MySQL -> postgresql, поэтому мы ничего не потеряли .
Томас
Или postgres / postgis -> sqlite /atialite ... Это была боль в заднице ...
Филипп
это происходит в тестах JUnit
Качанов
17

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

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
@ Майкл: Я бы не стал, если бы не было измерений, показывающих, что это было быстрее , а не просто короче .
дсимча
В большинстве ситуаций троичный оператор более читабелен, чем if. Упорство на высказываниях над выражениями на языке C является культурной / религиозной догмой, а не какой-либо объективной практикой. (Лучшее указание: если вложенная троичная if
форма
2
Проблема здесь в том, чтобы взять целую функцию и заменить ее одним оператором, возвращением, тем самым заменяя всю логику всей функции на вложенные троичные. Если бы вы увидели это, вы бы поняли. Это не религиозная вещь "Я ненавижу троичных операторов". Я не говорю о том, чтобы взять сингл ifв функции и заменить его троичным. Это хорошо, и часто более читабельно. Я говорю о замене целого 30-строчного метода одним оператором возврата и вложенными троичными. Никто не думал, что новый код был более читабельным, но один разработчик думал, что это быстрее.
Эдди
15

На одном из моих первых заданий в качестве полноправного разработчика я занялся проектом программы, которая страдала от проблем масштабирования. Он работал бы достаточно хорошо на небольших наборах данных, но полностью потерпел бы крах при наличии большого количества данных.

Пока я копался, я обнаружил, что оригинальный программист стремился ускорить процесс, распараллеливая анализ - запуская новый поток для каждого дополнительного источника данных. Однако он допустил ошибку в том, что для всех потоков требовался общий ресурс, на котором они находились в тупике. Конечно, все преимущества параллелизма исчезли. Более того, большинство систем рухнуло, чтобы запустить более 100 потоков только для блокировки всех, кроме одной. Моя мощная девайс-машина была исключением из-за того, что она прошла через набор из 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

Это может быть на более высоком уровне, чем то, что вы делали, но исправление (если вам позволено) также включает больший уровень боли:

Настаивайте на том, чтобы вручную запустить Диспетчер объектных отношений / Уровень доступа к данным вместо использования одной из установленных, протестированных, зрелых библиотек (даже после того, как они были вам указаны).

Гордон Хартли
источник
Это не всегда плохая идея свернуть свой собственный код. Как однажды сказал мудрый человек, найдите зависимости и устраните их. Если это основная бизнес-функция, сделайте это сами.
Кибби
Я никогда не предполагал, что это всегда плохая идея. Если вы не говорите, Франс Боума или что-то подобное, я сомневаюсь, что материал ORM / DAL является основной бизнес-функцией. Написание собственного эквивеланта, неэффективного с точки зрения затрат, является случаем переизобретения (квадратного) колеса, обычно из-за синдрома НИЗ.
Гордон Хартли
@ Кибби - я согласен. Лучше накатить свою собственную и понять это, чем использовать сторонние зависимости. Когда он сломается (и будет), по крайней мере, вы не сможете это исправить. В прошлом я обнаружил ошибки в Hibernate и Apache Commons, которые абсолютно убивали производительность нашего приложения.
CodingWithSpike
4
Скручивание руки на самом деле является единственным вариантом, если ни один из известных не обладает необходимой вам критической функцией.
staticsan
3
На самом деле, учитывая некоторые из вышеприведенных комментариев, есть еще несколько перспектив: еще одна пессимизация - попытаться заставить ORM сделать абсолютно все. Его часто полезно в 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ошибки раздражают, но это не лучшее решение для этого.

Chetan Sastry
источник
Каково лучшее решение тогда? Я думаю, что писать код там, где иногда случаются ошибки, не так просто, даже если они не имеют прямых последствий. Конечно, вы не должны этого делать, если не ожидаете, что объект будет нулевым в любых обстоятельствах - возможно, это то, что вы имели в виду.
Симон
7

Как насчет ЯГНИ, экстремизм. Это форма преждевременной пессимизации. Кажется, что каждый раз, когда вы применяете YAGNI, вам это в конечном итоге понадобится, что в 10 раз увеличит ваши усилия по добавлению, чем если бы вы добавили его в начале. Если вы создаете успешную программу, то, скорее всего, вам это нужно. Если вы привыкли создавать программы, жизнь которых заканчивается быстро, продолжайте практиковать ЯГНИ, потому что тогда я полагаю, ЯГНИ.

Замочить
источник
3
Спасибо, мне надоели эти неубедительные аббревиатуры «экстремального программирования» и то, как люди используют их для поддержки ленивых, непродуктивных практик.
JAL
Исследования реальных проектов показывают, что фактический коэффициент между одноразовым и реусбальным кодом в среднем составляет около 3. Таким образом, 10 - это всего лишь «ощущаемое» значение, но вы правы по своему намерению.
Петерчен
@peterchen - вы говорите, что исследования показывают, что для написания кода многократного использования в качестве одноразового кода требуется в три раза больше времени, или что они показывают, что преобразование одноразового кода в повторно используемый код занимает в три раза больше времени, чем для записи многоразовый код на первом месте?
Джефф Стерн
@jeff: IIRC они сравнили некоторую меру сложности (что бы вы ни думали о них) встроенных фрагментов, которые были перенесены в отдельные методы. Сложность возрастает из-за поддерживаемых дополнительных случаев, проверки параметров и т. Д. (Что позволяет предположить, что методы были довольно небольшими). Позвольте мне попытаться найти ссылку.
Петерчен
6

Не совсем преждевременная оптимизация - но, безусловно, ошибочная - это было прочитано на сайте BBC, из статьи, обсуждающей Windows 7.

Г-н Керран сказал, что команда Microsoft Windows изучала все аспекты операционной системы, чтобы внести улучшения. «Мы смогли сократить время выключения на 400 миллисекунд, слегка подрезав музыку выключения WAV-файла.

Я еще не пробовал Windows 7, поэтому могу ошибаться, но готов поспорить, что есть и другие проблемы, которые важнее, чем время, необходимое для завершения работы. В конце концов, когда я вижу сообщение «Завершение работы Windows», монитор выключается, и я ухожу - как мне помогают эти 400 миллисекунд?

belugabob
источник
Вы, вероятно, обнаружите, что другие проблемы не так легко объяснить непрограммистам на сайте BBC.
Том Лейс
Теперь это угол, который я не
учел
Эти 400 мс - 400 мс потребляемой мощности. Вероятно, незначительный, но, возможно, со временем он складывается. Тем не менее, не то, о чем я бы беспокоился
ZachS
1
Я потерял много часов, ожидая выключения виртуальных машин XP, чтобы я мог перейти к следующему. Я очень благодарен за быстрое отключение.
Джеймс
1
Интересно, что WAV-файлы воспроизводятся асинхронно, поэтому, пока фанфары выключения меньше, чем время, необходимое для выключения, обрезка WAV-файла ничего не делает. И что еще интереснее, если они настолько оптимизировали выключение, то почему каждому Windows-выключателю, который я выключил, нужны эоны, пока он действительно не выключится? (За исключением использования большой красной кнопки, конечно.)
TheBlastOne
6

Кто-то в моем отделе однажды написал строковый класс. Интерфейс какCString , но без зависимости от Windows.

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

Вместо этого каждый отдельный +=вызов вызывал перераспределение, которое превращало повторяющиеся добавления в алгоритм Шлеймеля-живописца O (n²) .

dan04
источник
5

Моему бывшему сотруднику (собственно говоря ) было поручено создать новый модуль для нашей Java ERP, который должен был собирать и анализировать данные клиентов (розничная торговля). Он решил разделить поле КАЖДЫЙ календарь / дата / время на его компоненты (секунды, минуты, часы, день, месяц, год, день недели, биместер, триместр (!)), Потому что «как еще мне запросить« каждый понедельник »?»

Joril
источник
3
Это не преждевременная оптимизация, он думал, что ему нужно сделать это для корректности
Pyrolistic
Конечно, он думал , что ему это нужно, но, поскольку большинство СУБД имеют какую-то функцию DAYOFWEEK (временная метка), на мой взгляд, делать это
заранее
1
Я бы не стал использовать его для OLTP, но если бы вы «анализировали данные клиента», то это на самом деле очень гибкий способ проектирования хранилища данных (при условии, что дата и время разделены на разные измерения). Вы действительно хотите вызвать DAYOFWEEK () для миллионов строк данных или просто выполнить поиск индекса по целочисленному полю?
Тим Медора
Ну, я не знаю, было ли столько строк, но, конечно, это не то объяснение, которое было дано :)
Joril
3

Никому не в обиду, но я просто оценил задание (java), в котором было это

import java.lang.*;
переполненный
источник
1
Если это не класс высшего уровня, я думаю, что вам нужно немного расслабиться, если вы не научите ее достаточно, чтобы понять, почему это не очень хорошая идея.
Брайан Окли
24
Собираюсь ли я быть единственным, кто заметит иронию учителя, звонящего по WTF, в коде ученика, которого он / она отвечает за обучение правильному программированию?
JohnFx
3
Да, я не вижу, что это повредит. В худшем случае это излишне. Студенты, как правило, прибегают к жесткой последовательности во время обучения, и импорт java.lang строго соответствует тому, что студент узнал об импорте.
Cygil
1
Спасибо всем за то, что рассказали мне очевидное. Это было задание по вычислительной биологии, и я его не считал и даже не упоминал.
переполнен
2
@JohnFX: грейдер и учитель не всегда один и тот же человек.
Эдди