Все мы знаем, что преждевременная оптимизация - это корень всех зол, потому что она приводит к нечитаемому / неподдерживаемому коду. Еще хуже пессимизация, когда кто-то реализует «оптимизацию», потому что они считать, она будет быстрее, но в конечном итоге будет медленнее, а также будет глючной, неподдерживаемой и т. д. Каков самый нелепый пример этого, который вы видели ?
На всякий случай, если вы не знали, они говорили о вашей ветке здесь, в последнем подкасте.
Файл жаргона: catb.org/jargon/html/P/pessimal.html, catb.org/jargon/html/P/pessimizing-compiler.html


Я признаю, что ничего грандиозного, но я поймал людей, использующих 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 синхронизирован, поэтому на самом деле возникают дополнительные накладные расходы, если вы объединяете только несколько строк. (Не говоря уже о том, что разница в этой шкале совершенно тривиальна.) Два других момента об этой практике:
Во-первых: если вы не используете Java 5, у вас нет StringBuilder. Во-вторых: нельзя рассчитывать на оптимизацию JVM. Принимая во внимание первый и второй оператор, первый оператор СОЗДАЕТ 5 ОБЪЕКТОВ, а второй создает один, а возможно, и два объекта.
Второй момент: компиляторы могут использовать StringBuffer за кулисами в любом случае: java.sun.com/docs/books/jls/third_edition/html/expressions.h tml
Во-первых: почему бы вам не использовать хотя бы Java 5? Во-вторых: да, можно. Как получилось, что в первом примере можно сосчитать до 5, а во втором - нет? Он использует те же строковые литералы, что и первый. Напишите читаемый код и позвольте компилятору решить, когда использовать StringBuffer за кулисами.
Второй использует один StringBuffer, а затем создает String. Это два объекта. Первый создает экземпляр StringBuffer для каждой объединенной строки. Во-вторых, политика рабочего пространства иногда препятствует использованию Java 5. Например, при развертывании на Websphere 6.0 вы ограничены Java 1.4.
Я видел то же самое в .NET (ТОЛЬКО отличие - это имя класса StringBuffer -> StringBuilder)
@ MetroidFan2002: Строковые литералы во втором примере тоже являются объектами. Как я уже сказал в ответе, в этом масштабе различия тривиальны.
@Bill - во втором примере нет другого StringBuffer для каждой строки. Первый пример может быть (за кулисами) эквивалентом String msg = new StringBuffer ("Count = ") .append (new StringBuffer (count) .append (...) - new StringBuffers для каждой строки. Во втором примере нет не делай этого.
@ MetroidFan2002: Раздел 15.18.1.2 из вашей ссылки выше: Реализация может выбрать выполнение преобразования и конкатенации за один шаг, чтобы избежать создания, а затем отбрасывания промежуточного объекта String. (продолжение ...)
Чтобы повысить производительность повторяющейся конкатенации строк, компилятор Java может использовать класс StringBuffer или аналогичный метод для уменьшения количества промежуточных объектов String, которые создаются путем оценки выражения.
Это не означает, что он заменяет каждую строку своим собственным StringBuffer. Оптимизация, выполняемая компилятором, сокращает количество создаваемых объектов.
Первый метод занимает одну строку и не вызывает вызова. Второй метод занимает 6 строк, 5 вызовов методов и один новый вызов. Прежде всего, читаемость!
@Eric: String msg = "Count = " + count + "of" + total + "."; часто компилируется в Java в String msg = new StringBuffer (). append ("Count"). append (count) .append ("of") .append (total) .append ("."). toString (); ... именно это и делает второй пример.
@ Эрик: Подтверждено. javac 1.6.0_12 декомпилируется в String s = (new StringBuilder ()). append ("Count = ") .append (i) .append ("of") .append (j) .append ("."). toString ( ); - ШЕСТЬ вызовов методов и один новый вызов. Не говорить, что явный SB лучше, но + конкатенация не удаляет вызовы методов, а просто скрывает их.
Г-н Вагнер, дело в том, что ВЫ должны смотреть на все эти вызовы методов, а не на компилятор. Вы должны написать их, а потом осмыслить. В любом случае компилятор делает то же самое. Так что удобочитаемость в данном случае важнее.
+1 Я вижу даже лучше. Использование StringBuilder для объединения группы строк константы в VB.NET вместо объединения их с помощью &. Последний излучает только одну большую строковую константу в MSIL, первый создает довольно тяжелый объект и вызывает его методы. Для каждого запроса.
Я был техническим обозревателем книги по программированию, показывающей эту пессимизацию. Конечно, я немедленно сообщил об этом!
Я видел людей, использующих alphadrive-7 для полной инкубации CHX-LT. Это необычная практика. Более распространенной практикой является инициализация трансформатора ZT, чтобы уменьшить буферизацию (из-за большей устойчивости к сетевой перегрузке) и создать байтографию в стиле Java.
Совершенно пессимистично!
возможно они пытались усилить конденсатор потока
Итак, по сути, единственный задействованный новый принцип заключается в том, что вместо энергии, генерируемой относительным движением проводников и потоков, она создается модиальным взаимодействием магниторезирующего сопротивления и емкостного сопротивления?
+1 потому что мой монитор все равно нуждался в чистке ;-)
Черт!! А как насчет электромагнитно-кросс-генетического эффекта. Думаю, это тоже нужно учитывать. Или субъект может превратиться в зомби.
Три слова: «Хромированные подшипники глушителя».
Базы данных - это игровая площадка для пессимизации.
Избранное включает:
Это не в моей голове.
О том, чтобы сопротивляться необходимости индексировать, просто больно думать.
Да, я знаю кого-то, кто работает в крупной нефтяной компании США, где почти все их таблицы имеют связанную архивную таблицу, и большинство запросов выбирают из представлений, которые объединяют пары таблиц. Производительность такая, как и следовало ожидать!
Ха, я думаю, что каждый администратор баз данных в какой-то момент должен был пойти по пути объединения с архивной таблицей. В то время это всегда казалось разумным так.
Добавляю: разбить базу данных на несколько разных баз (клиенты a-c, клиенты d-f и т. д.)
Не могли бы вы подробнее рассказать о «Денормализовать в поля как массив»? Что вы здесь имеете в виду?
Поля как массив - добавление столбцов в таблицу, скажем, для каждого месяца года для ежемесячной активности; или столбцы для больших, средних и малых размеров; или красный, синий, зеленый для цветов. Нормализация требует новой дочерней таблицы для этих атрибутов.
@le dorfier теперь мне приходит в голову, что обычно идея поля как массива реализуется без добавления столбцов. Они просто кодируют массив в существующий столбец, создавая огромные постоянно растущие строки XML и сохраняя их в столбце. Вздох. Год спустя они инвестируют в аппаратный модуль обработки XML. Это никогда не закончится.
Это может быть на более высоком уровне, чем то, что вам нужно, но исправление этого (если вам разрешено) также связано с более высоким уровнем боли:
Настаивать на том, чтобы вручную развернуть диспетчер отношений между объектами / уровень доступа к данным вместо использования одной из установленных, протестированных, зрелых библиотек (даже после того, как они были вам указаны).
Накручивать собственный код - не всегда плохая идея. Как однажды сказал один мудрый человек, найдите зависимости и устраните их. Если это основная бизнес-функция, делайте это самостоятельно.
Я никогда не думал, что это всегда плохая идея. Я сомневаюсь, что ORM / DAL - это основная бизнес-функция, если вы не скажете Франса Баумы или ему подобных. Крайне неэффективно писать собственный эквивалент - случай заново изобрести (квадратное) колесо, обычно из-за синдрома NIH.
@Kibbee - согласен. Лучше бросить свой собственный и понять это, чем использовать сторонние зависимости. Когда он сломается (а он сломается), по крайней мере, вы сможете его исправить. В прошлом я обнаруживал ошибки в Hibernate и Apache Commons, которые полностью убивали производительность нашего приложения.
Дело в том, что приличную ORM очень сложно написать. Конечно, моделировать записи как объект с помощью удобных методов вставки / обновления легко, но как только вы попытаетесь предоставить API, который поддерживает объединения и создает эффективный SQL, вы попадаете в мир боли.
Ручная прокатка - действительно ваш единственный вариант, если ни один из известных не имеет критически важной функции, которая вам нужна.
На самом деле, с учетом некоторых из вышеперечисленных комментариев, некоторые более перспективные: еще одна пессимизация - это попытаться заставить ORM делать абсолютно все. Часто это полезно в 95% + случаев. Для этих последних 5% гораздо проще отказаться от созданного вручную кода сохраняемости / прямых вызовов хранимых процедур и т. д. Для повышения производительности, простоты или того и другого.
Однажды я работал над приложением, полным такого кода:
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;
Увеличена производительность приложения вдвое.
Однажды я работал в компании, где правила кодирования требовали «только один возврат в конце» (для поддержки). И действительно, некоторые выплевывают код, как ваш, потому что они не думают (очевидные решения в большинстве случаев заключались в использовании перехода к выходу из процедуры или изменения условия выхода циклов)
Место, в котором был этот код, также имело правило «один возврат в конце».
Здесь return curr ведет к заметно иному поведению. Когда вы возвращаете curr, вы получаете ПЕРВОЕ совпадение, а вставленный вами код возвращает ПОСЛЕДНЕЕ совпадение.
@SoapBox: Вы правы. @Dour High Arch: повышение производительности не имело ничего общего с правилом единственного возврата, как сказал Флоло, изменение условия цикла на (curr &&! Found) будет иметь тот же эффект. ПЕРЕЙТИ к выходу из процесса ужасен и сводит на нет цель единственного возврата.
Использование break; было бы лучше, чем добавлять дополнительное условие к while ().
Хорошие комментарии всем. В этом случае предполагалось, что будет только один кортеж с каждым идентификатором.
Это не имеет ничего общего со стандартом кодирования и полностью связано с ошибкой кодирования. Конечно, возврат сработал бы, но добавление пары фигурных скобок и оператора break также сработало бы.
В старом коде действительно есть ошибка производительности, связанная с добавлением "break;" исправил бы красиво.
@Akusete: На самом деле, я не думаю, что GOTO будет особенно вредным. На процедуру он действует не иначе, чем на перерыв. GOTO не всегда вреден, он вреден только тогда, когда он вызывает нарушение инвариантов входа в метод.
@Chris R: Использование GOTO создает дополнительную нагрузку на программиста по обслуживанию - они должны определить, что это на самом деле просто замаскированный перерыв. Кроме того, возможно, что goto, который сегодня действует как break, пропустит некоторый код, добавленный сразу после цикла завтра. Я говорю, будь проще
Но это же не «пессимизация», не так ли? Это просто ожидаемая оптимизация.
Ваша версия возвращает первое совпадение, а исходная функция возвращает последнее совпадение.
Я думаю, что фраза «преждевременная оптимизация - корень всех зол» слишком часто используется. Для многих проектов стало оправданием не принимать во внимание производительность до самого конца проекта.
Эта фраза часто становится опорой для людей, избегающих работы. Я вижу эту фразу, когда люди действительно должны сказать: «Да ну, мы действительно не думали об этом заранее, и сейчас у нас нет времени разбираться с этим».
Я видел гораздо больше «нелепых» примеров глупых проблем с производительностью, чем примеров проблем, возникших из-за «пессимизации».
Я думаю, что лучше всего будет следующее утверждение: «Оптимизация без измерения и понимания - это вообще не оптимизация - это просто случайное изменение».
Работа над хорошей производительностью отнимает много времени - часто больше, чем разработка самой функции или компонента.
Странно, что за это проголосовали отрицательно. Во всяком случае, информативный пост.
«Преждевременное» - ключевое слово в этой цитате. Вы перефразировали это словом «оптимизация без измерения и понимания», похоже, ни на что не меняют смысла. Именно это имел в виду Кнут.
Re: упаковка структур данных ... Я знал парня, который всегда ставил #pragma pack 1 во главе каждого исходного файла. В конечном итоге он укусил его на какой-то платформе, которая требовала выравнивания значений DWORD, когда он передавал указатель на член структуры DWORD и вместо этого получал случайные значения.
@Foredecker: отлично. Слишком многие люди забывают контекст, который ставит эту цитату категорически против микро-оптимизации. Анализ проблемы для выбора правильного алгоритма перед ее реализацией не преждевременно, но слишком часто эта цитата выдвигается, чтобы оправдать самое ленивое и самое неэффективное решение.
Привет, Билл-ящерица :) Я полностью понимаю, о чем вы - это то, что имел в виду Кнут. Но сама фраза НАМНОГО переоценена.
Опечатка: "опрос" вместо "опрос" извините, ничего не могу с собой поделать :) Все равно +1
+1 Один пример из какого-то кода, над которым мне нужно работать - чтение одного и того же значения из базы данных 100 раз за ОДНУ операцию. Я постепенно работаю над его рефакторингом, но это сложно.
@Foredecker: Я понимаю, что вы имеете в виду, говоря о злоупотреблении этой фразой. Если вы говорите это вслух, это, вероятно, знак того, что вам нужно измерить свой код. :)
Это действительно зависит от конкретного случая, случаев, когда преждевременная оптимизация становится проблемой, больше, чем неадекватное планирование оптимизации становится проблемой.
-1: Есть разница между «оптимизацией» и правильным дизайном. Для тех, кто не может сказать, хорошее практическое правило состоит в том, что «оптимизация» делает код более сложным для чтения, но более быстрым или эффективным. Лучший дизайн сделает код более простым для чтения (или, по крайней мере, не хуже) и более эффективным.
Я полностью согласен. Я сообщил об ошибке в Microsoft, когда библиотека .NET выполняла некоторое взаимодействие и вызывала LoadLibrary сотни и сотни раз, когда ей требовалось всего около 30 вызовов. Я написал аналогичный код, который занимал секунды, а не минуты. MS не использовала это исправление. :)
Привет, Тид :) Я полностью не согласен - в исправлении ошибок производительности (что можно назвать оптимизацией) нет ничего, что затрудняло бы чтение кода.
Если его слишком часто используют, то население, задающее вопросы по SO, в значительной степени склоняется к выбросам. : D
Этим злоупотребляют, и мне тоже надоела эта фраза! Я как раз думал об этом вчера!
«Тратить мегабайты памяти на напрасное сохранение полных путей к файлам» - это так забавно в этом контексте. Хотя, полагаю, непреднамеренно?
Я вижу, что это использовалось для постоянного оправдания полной траты ЦП.
Этим злоупотребляют, потому что это отборная цитата. Вам нужно прочитать весь абзац и контекст. Например, он не включает выбор алгоритма, дизайн индекса базы данных, ...
Давай, ребята. Это «преждевременная оптимизация Любовь - корень всех зол».
Однажды я видел базу данных MSSQL, в которой использовалась «Корневая» таблица. В корневой таблице было четыре столбца: GUID (uniqueidentifier), ID (int), LastModDate (datetime) и CreateDate (datetime). Все таблицы в базе данных имеют внешний ключ к корневой таблице. Всякий раз, когда новая строка создавалась в таблице Любые в базе данных, вам приходилось использовать пару хранимых процедур для вставки записи в корневую таблицу, прежде чем вы могли добраться до фактической таблицы, о которой вы заботились (вместо того, чтобы база данных выполняла работу для вы с несколькими триггерами простые триггеры).
Это создавало бесполезный бесполезный слух и головную боль, требовало, чтобы что-то написанное поверх этого использовало sprocs (и устраняло мои надежды на внедрение LINQ в компании. Это было возможно, но просто не стоило головной боли), и в довершение всего не было. Я даже не сделал то, что должен был сделать.
Разработчик, выбравший этот путь, защищал его, полагая, что это сэкономило массу места, потому что мы не использовали Guids в самих таблицах (но ... разве GUID не создается в корневой таблице для каждой создаваемой нами строки?) , как-то улучшили производительность и упростили аудит изменений в базе данных.
Да, и диаграмма базы данных выглядела как паук-мутант из ада.
Я думаю, что нет абсолютного правила: некоторые вещи лучше всего оптимизировать заранее, а некоторые нет.
Например, я работал в компании, где мы получали пакеты данных со спутников. Каждый пакет стоит больших денег, поэтому все данные были оптимизированы (т. Е. Упакованы). Например, широта / долгота отправлялись не как абсолютные значения (с плавающей запятой), а как смещения относительно «северо-западного» угла «текущей» зоны. Нам пришлось распаковать все данные, прежде чем их можно было использовать. Но я считаю, что это не пессимизация, а интеллектуальная оптимизация для снижения затрат на связь.
С другой стороны, наши разработчики программного обеспечения решили, что распакованные данные должны быть отформатированы в хорошо читаемый XML-документ и сохранены в нашей базе данных как таковые (в отличие от того, чтобы каждое поле сохранялось в соответствующем столбце). Их идея заключалась в том, что «будущее за XML», «дешевое дисковое пространство» и «дешевый процессор», поэтому не было необходимости что-либо оптимизировать. В результате наши 16-байтовые пакеты были преобразованы в документы размером 2 КБ, хранящиеся в одном столбце, и даже для простых запросов нам приходилось загружать в память мегабайты XML-документов! Мы получали более 50 пакетов в секунду, так что вы можете себе представить, насколько ужасной стала производительность (кстати, компания обанкротилась).
Итак, опять же, не существует абсолютного правила. Да, иногда слишком ранняя оптимизация - это ошибка. Но иногда девиз «ЦП / дисковое пространство / память дешево» является настоящим корнем всех зол.
Я согласен, что «процессор / дисковое пространство / память дешевы» - это настоящий корень всех зол. +1
Я тоже слышал эту чушь XML. Еще одна затонувшая рота.
@ksuralta: «ЦП / дисковое пространство / память дешевы» - удобный предлог, чтобы не думать. Избегание мыслей - это мнимый корень всех зол.
Эта XMLизация произошла и на моем рабочем месте, после чего последовала JSONization. Все для того, чтобы избежать «трудоемкого» проектирования реляционных баз данных.
Моему бывшему коллеге (на самом деле s.o.a.b.) было поручено создать новый модуль для нашей Java ERP, который должен был собирать и анализировать данные о клиентах (розничная торговля). Он решил разделить КАЖДОЕ поле календаря / даты и времени на компоненты (секунды, минуты, часы, день, месяц, год, день недели, биметр, триместр (!)), Потому что «как еще я мог бы запросить« каждый понедельник »?»
Это не преждевременная оптимизация, он подумал, что это нужно сделать для правильности.
Конечно, он мысль, ему это было нужно, но поскольку в большинстве СУБД есть какая-то функция DAYOFWEEK (timestamp), делать этот беспорядок заранее, на мой взгляд, достаточно преждевременно :)
Я бы не стал использовать его для OLTP, но если вы «анализируете данные клиентов», то на самом деле это очень гибкий способ проектирования хранилища данных (при условии, что дата и время разделены на разные измерения). Вы действительно хотите вызвать DAYOFWEEK () для миллионов строк данных или просто выполнить поиск по индексу для целочисленного поля?
Ну, я не знаю, было ли там столько строк, но, конечно, это не то объяснение, которое было дано :)
Ни в коем случае не обижайтесь, но я только что оценил задание (java), в котором было это
import java.lang.*;Если это не класс высшего уровня, я думаю, вам нужно немного ослабить эту ученицу, если вы не научили ее достаточно, чтобы понять, почему это не очень хорошая идея.
О, я не возражал против ученика, но мне было интересно, что заставит ученика включить строку (не похоже, чтобы она не компилировалась раньше)
Java достаточно умен, чтобы нет импортировать то, что ему не нужно, когда вы используете blob (*) для импорта. Это не так плохо, как ненужные #includes в C и C++. Тем не менее, тот факт, что студент чувствует необходимость импорта из java.lang, вызывает беспокойство. :)
Собираюсь ли я быть единственным, кто заметит иронию того, что учитель вызывает WTF из-за кода ученика, который он / она отвечает за обучение правильному программированию?
@JohnFx: невероятно забавно. Однако помните, что плохой учитель может иметь плохого ученика.
Да, я не вижу, чтобы это было больно. В худшем случае лишнее. Студенты склонны прибегать к жесткой согласованности во время обучения, и импорт java.lang строго соответствует тому, что студент узнал об импорте.
Спасибо всем за то, что рассказали мне очевидное. Это было задание по вычислительной биологии, и я его не считал и даже не упоминал.
@JohnFX: Оценщик и учитель - не всегда одно и то же.
Разве это не обязательно в C#, чтобы импортировать базовый языковой материал?
Это звучит как хорошая экономия времени для java-студентов, которые они могут использовать в шаблоне, когда они не знают наверняка, что они будут использовать ... конечно, окончательная представленная версия должна быть исправлена, но тем временем это PITA для постоянно добавлять / удалять импорт, потому что это отнимает много времени и отвлекает вас от текущей задачи. Как программист на C, Google foobar был интересным способом изучения Java, который требовал тестирования нескольких способов выполнения одной и той же задачи в соответствии с требованиями времени - каждый из которых требовал разного набора операций импорта. Я потратил почти столько же времени на поиск импорта, сколько на кодирование.
Использование регулярного выражения для разделения строки, когда достаточно простого string.split
НО в Java String.Split использует регулярное выражение!
Я не понимаю, как Regex может быть таким же быстрым, как внутреннее разделение строки.
Но намеренный поиск регулярного выражения, используемого для разделения строк, и замена его «простой» функцией разделения звучит как прекрасный пример пессимизации. Библиотеки регулярных выражений достаточно быстрые.
@ Дэвид Кроушоу: Поиск возможностей микрооптимизации тратит человеческое время; bud, когда код пишу, используйте наименее сложное достаточное решение.
-1: Если вы привыкли к регулярным выражениям, вполне естественно написать это вместо того, чтобы привыкать к внутренним строковым манипуляторам языка 1001.
Господи, думаю, я их всех видел. Чаще всего это попытка исправить проблемы с производительностью кем-то, кто слишком ленив, чтобы найти их ПРИЧИНУ этих проблем с производительностью или даже выяснить, действительно ли существует проблема с производительностью. Во многих из этих случаев я задаюсь вопросом, не является ли это просто случаем, когда человек хочет опробовать определенную технологию и отчаянно ищет гвоздь, который подошел бы их новому блестящему молотку.
Вот недавний пример:
Архитектор данных приходит ко мне с тщательно продуманным предложением вертикального разделения ключевой таблицы в довольно большом и сложном приложении. Он хочет знать, какие усилия по развитию потребуются, чтобы приспособиться к изменениям. Разговор проходил так:
Мне: Почему вы об этом думаете? Какую проблему вы пытаетесь решить?
Ему: Таблица X слишком велика, мы разбиваем ее по соображениям производительности.
Мне: С чего вы взяли, что он слишком широкий?
Ему: Консультант сказал, что слишком много столбцов для одной таблицы.
Мне: А это влияет на производительность?
Ему: Да, пользователи сообщали о периодических замедлениях работы модуля XYZ приложения.
Мне: Как узнать, что ширина таблицы является источником проблемы?
Ему: Это таблица ключей, используемая модулем XYZ, и она похожа на 200 столбцов. Это должно быть проблема.
Я (объясняя): Но модуль XYZ, в частности, использует большинство столбцов в этой таблице, а используемые им столбцы непредсказуемы, поскольку пользователь настраивает приложение для отображения данных, которые он хочет отобразить из этой таблицы. Вероятно, что в 95% случаев мы все равно объединим все таблицы вместе, что снизит производительность повредить.
Ему: Консультант сказал, что он слишком широк и нам нужно его изменить.
Мне: Кто этот консультант? Я не знал, что мы наняли консультанта, и они вообще не разговаривали с командой разработчиков.
Ему: Ну, мы их еще не наняли. Это часть предложения, которое они предложили, но они настаивали на том, что нам нужно изменить архитектуру этой базы данных.
Мне: Угу. Итак, консультант, который продает услуги по изменению дизайна базы данных, думает, что нам нужен редизайн базы данных ....
Разговор продолжался и продолжался так. После этого я еще раз взглянул на рассматриваемую таблицу и решил, что ее, вероятно, можно было бы сузить с помощью простой нормализации без необходимости в экзотических стратегиях разделения. Это, конечно, оказалось спорным вопросом, когда я исследовал проблемы с производительностью (о которых ранее не сообщалось) и отследил их до двух факторов:
Конечно, архитектор по-прежнему настаивает на вертикальном разбиении таблицы на «слишком широкую» мета-проблему. Он даже поддержал свою позицию, получив предложение от другого консультанта по базам данных, который смог определить, что нам нужны серьезные изменения в конструкции базы данных, не глядя на приложение или не проводя какого-либо анализа производительности.
Aaag MS Доступ к продукту. Мы написали процедуру, позволяющую разрывать все соединения доступа каждые несколько минут, чтобы наконец получить сообщение о том, что это плохо.
У нас была аналогичная работа, но она вышла из употребления. Честно говоря, Access - не проблема, он просто упрощает для новичков создание / выполнение неэффективных запросов.
В нашей компании у нас есть зависимость от устаревших специальных подключений Access к производственной БД. Нет ничего лучше, чем несколько случайных операторов SQL, которые забыли предложение WHERE и заблокировали основные таблицы!
"Я слышал, у лилового больше всего RAM"
Это могло бы быть и хуже. Редактор запросов Excel блокирует всю базу данных при ее использовании. Когда я не знал об этом, я оставлял один экземпляр открытым на большую часть дня, пока работал над чем-то другим. Хуже всего то, что MS SQL Server не сообщил правильное имя пользователя / машину, которая производила блокировку. Через несколько часов я понял, что причиной блокировки был я, потому что заблокированные таблицы были частью представления, которое я запрашивал, и сначала проверил все остальное.
Проверка перед КАЖДОЙ операцией 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 меня раздражают, но это не лучшее решение для этого.
Какое тогда лучшее решение? Я считаю небрежным писать код, в котором время от времени возникают ошибки, даже если они не имеют прямых последствий. Конечно, вам не следует этого делать, если вы не ожидаете, что объект будет нулевым ни при каких обстоятельствах - возможно, вы это имели в виду.
Худший пример, который я могу придумать, - это внутренняя база данных в моей компании, содержащая информацию обо всех сотрудниках. Он получает еженощные обновления от отдела кадров и имеет поверху веб-службу ASP.NET. Многие другие приложения используют веб-службу для заполнения таких вещей, как поля поиска / раскрывающегося списка.
Пессимизм заключается в том, что разработчик думал, что повторные вызовы веб-службы будут слишком медленными, чтобы делать повторяющиеся запросы SQL. И что он сделал? Событие запуска приложения считывает всю базу данных и преобразует все это в объекты в памяти, которые хранятся на неопределенный срок, пока пул приложений не будет перезапущен. Этот код был настолько медленным, что на его загрузку ушло менее 2000 сотрудников. Если вы непреднамеренно перезапустили пул приложений в течение дня, это может занять 30 минут или больше, потому что каждый запрос веб-службы будет запускать несколько одновременных перезагрузок. По этой причине новые сотрудники не будут отображаться в базе данных в первый день создания их учетной записи и, следовательно, не смогут получить доступ к большинству внутренних приложений в первые несколько дней своей работы.
Второй уровень пессимизма заключается в том, что менеджер по разработке не хочет трогать его из опасения сломать зависимые приложения, но все же у нас по-прежнему случаются спорадические отключения критически важных приложений в масштабах всей компании из-за плохой разработки такого простого компонента.
Лучшее управление - «Нет, давайте не будем тратить 80 часов программиста на исправление этого приложения, это слишком дорого. Давайте просто сохраним его, чтобы его ошибки могли истощить 200+ пользовательских часов в месяц, плюс 10 часов программиста в месяц на 'техническое обслуживание'." АААААААААААА !!!
«Независимость базы данных». Это означало отсутствие хранимых процедур, триггеров и т. д. - даже никаких внешних ключей.
Является ли эта «независимость» в том смысле, что вы так далеко от базы данных, что забыли, что такое данные? Излишне абстрагироваться от баз данных «во избежание миграционных болей» - это любимая мозоль; тебе это не понадобится.
Довольно много. Астронавты архитектуры за работой. Я создавал веб-приложения с тех пор, как появился Интернет, и за все это время я фактически никогда не переходил с одной платформы баз данных на другую.
+1, Крис: Я работаю над термоусадочной пленкой и веб-приложениями с 1998 года, и мне ни разу ни разу не понадобилось менять продукты СУБД (за исключением поддержки Sybase ASE и SQL Server, что довольно просто делать)
Это случается. Ок. 10 лет назад мне пришлось поддерживать какое-то программное обеспечение, которое пришлось перейти с Sybase на Oracle. Другой известный мне проект в настоящее время находится в процессе перехода с Sybase на MySQL.
Я уверен, что это случается, но достаточно редко вы становитесь идиотом, если проектируете свою архитектуру с учетом этой возможности.
довольно много. тонны людей также приводят этот аргумент о «независимости базы данных». выберите базу данных, изучите ее и извлеките выгоду из нее.
Правда, ребята? А как насчет продуктов для перепродажи, таких как FogBugz, который поддерживает SQL Server, Access и MySql? Делает ли это Джоэла «идиотом» или «астронавтом-архитектором»?
В самом деле, я работал над многочисленными проектами, которые должны были (или решили) изменить dbs. Однако у нас не было глупых правил, позволяющих избегать внешних ключей, поскольку мы предполагали, что любая используемая нами база данных будет иметь хотя бы некоторую разумность в качестве СУБД. И портирование триггеров производилось при необходимости (хотя и избегалось).
harpo, это другая ситуация - в таком случае это требование. Я говорю о тех случаях, когда это не является требованием, но AA решает, что это «может быть» в какой-то момент.
@All: Независимость от БД может стоить вам, да, но наш продукт работает в средах, где поставщик БД выбирается на основе ставок, и нам в основном приходится подыгрывать. Некоторые разработчики не могут позволить себе роскошь вертикально интегрированного программного стека и вынуждены довольствоваться этим.
Почему бы просто не использовать MySQL, MariaDB или PostgreSQL? Все бесплатно, покупка не требуется.
@Demetri: очевидно, вы никогда не работали в правительственной среде, где платная поддержка коммерческого продукта в основном является обязательным требованием. Выбор продукта без того, чтобы кого-то винить, ограничивает карьеру в бюрократическом мышлении.
Я понимаю, в чем проблема - вас обвинят, если винить некую компанию. Если вы использовали Postgres, вы все равно могли бы распространять СУБД вместе с продуктом (Postgres имеет лицензию BSD), хотя вам потребуется предоставить / заключить договор на поддержку.
@chris Теперь я понимаю - нельзя выбирать СУБД, потому что за нее надо платить, а значит выставлять ставки.
Однажды мне пришлось попытаться изменить код, который включал эти драгоценные камни в класс Constants.
public static String COMMA_DELIMINATOR = ",";
public static String COMMA_SPACE_DELIMINATOR = ", ";
public static String COLIN_DELIMINATOR = ":";
Каждый из них многократно использовался в остальной части приложения для разных целей. COMMA_DELIMINATOR засорял код более чем 200 использованием в 8 различных пакетах.
По крайней мере, что-то подобное легко найти / заменить из источника - тем не менее, мои соболезнования.
Также - Делиминатор? Я думал, что это написано «разделитель». Delimiter звучит как плохой фильм середины 90-х, в котором каким-то образом было 3 сиквела ...........
Разделитель III: Возвышение запятых
С другой стороны, я рад видеть правильное разграничение Colins. Каждый достойный программист знает, что если и есть что-то, что нужно выделить как следует, так это чертов Колинз.
Найти и заменить правильно не так-то просто. Поскольку каждый используется для разных целей. Любой хороший программист сделал бы хотя бы что-то вроде этого: COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ... и т. д.
Когда эта запятая становится точкой с запятой, константа превращает потенциально трудоемкую задачу в несколько щелчков мышью в среде IDE.
Я собирался упомянуть StringBuilder для крошечных / нециклических строковых конкатенаций, но он был упомянут.
Помещение переменных метода в закрытые члены класса, чтобы предотвратить их «сборку мусора при каждом запуске метода». Переменные являются типами значений.
Может быть, просто беглый взгляд на систему на раннем этапе поможет указать на возможные узкие места.
«Эта часть не должна быть быстрой» (архивирование журналов) «Эта часть должна быть очень быстрой» (принятие новых подключений)
Тогда очень быстрые части обычно не нуждаются в дополнительной оптимизации с грязными причудами, обычно достаточно приличного оборудования и хорошо закодированных частей.
Просто отвечая на простой вопрос: "Получу ли я что-нибудь от очень быстрой этой части кода?" будет отличным ориентиром. Я имею в виду, что здравый смысл оптимизирует другие части проекта!
Похоже, никто не упомянул о сортировке, так что я скажу.
Несколько раз я обнаруживал, что кто-то вручную создал пузырьковую сортировку, потому что ситуация «не требовала» вызова «слишком причудливого» алгоритма быстрой сортировки, который уже существовал. Разработчик остался доволен, когда созданная им вручную пузырьковая сортировка достаточно хорошо проработала с десятью строками данных, которые они используют для тестирования. После того, как заказчик добавил пару тысяч строк, все прошло не так хорошо.
Я сделал это сам однажды, когда определил, что обычно n = 2. Более поздние улучшения продукта опровергли мою предпосылку, и код был заменен PDQ.
Да, но приятно время от времени писать что-то на основе алгоритм;)
ой. причудливая стандартная быстрая сортировка, которая фактически разветвляется на пузырьковую сортировку, если N мало.
Как насчет экстремизма ЯГНИ. Это форма преждевременной пессимизации. Кажется, что всякий раз, когда вы применяете YAGNI, он вам в конечном итоге нужен, что приводит к 10-кратным усилиям, чтобы добавить его, чем если бы вы добавляли его в начале. Если вы создадите успешную программу, скорее всего, она вам понадобится. Если вы привыкли создавать программы, жизнь которых быстро заканчивается, продолжайте практиковать ЯГНИ, потому что тогда, я полагаю, ЯГНИ.
Спасибо, мне надоели эти убогие аббревиатуры «экстремальное программирование» и то, как люди используют их для поддержки ленивых, контрпродуктивных практик.
Исследования реальных проектов показывают, что фактический коэффициент между одноразовым и повторно используемым кодом в среднем составляет около 3. Итак, 10 - это просто «ощутимое» значение, но вы правы по своему намерению.
@peterchen - вы говорите, что исследования показывают, что для написания многократно используемого кода требуется в три раза больше времени, чем для одноразового кода, или что они показывают, что на одноразовый код перерабатывать для многократного использования требуется в три раза больше времени, чем на написание многоразовый код в первую очередь?
@jeff: IIRC они сравнили некоторую меру сложности (что бы вы ни думали о них) встроенных сниппетов, которые были перемещены в отдельные методы. Сложность увеличивается из-за поддержки дополнительных случаев, проверки параметров и т. д. (Что заставляет меня предполагать, что методы были довольно небольшими). Разрешите попробовать отыскать ссылку.
Приложение, которое использовало целочисленное поле для выделения побитовый, в какие группы доступа к приложениям наши клиенты могли добавлять своих пользователей. Это означало, что в то время мы могли создать в общей сложности 32 группы для всех более чем 500 клиентов.
Аааа, но побитовое сравнение быстрее, чем равенство, и дааай быстрее, чем соединение, верно?
К сожалению, когда я полностью (и довольно громко) взбесился этим кодом и его автором, я обнаружил, что автор был моим начальником. Оказывается, довольно авторитарный чувак.
P.s.
Я знаю, о чем вы думаете, это должна была быть двоичная строка, верно? :)
Нет, я думаю, это не пример пессимизации, это неправильный дизайн для правильной функции. Невозможно оптимизировать кладж так, чтобы он работал, и пессимизация не повредила шоу, она остановила или предотвратила его.
У меня было точно такое же, где я работал. Забавно, как остальная система была в порядке. Я думаю, что это должно быть зависание от старожилов, пытающихся сохранить до последней капли памяти.
Любые значительные усилия по оптимизации, которые не основаны на упорядоченных отчетах из профилировщика, приносят мне большой WTF.
Будь осторожен. Профилирование имеет решающее значение, но это не способ Только выявить проблемы с производительностью.
Что ж, не дразни ... как насчет упоминания некоторых, если другие конкретные способы?
Поскольку вы конкретно сказали «инструмент профилировщика», я предполагаю, что он имеет в виду методы, которые не полагаются на возможность запуска всего приложения под вашим любимым профилировщиком. То есть, вход в ключевые места для поиска узких мест (разновидность ручного профилирования). Только так можно найти множество самых сложных проблем.
Важен не инструмент, его использует мозг героя (или нет). Это все равно, что сказать: «Я сделаю WTF вашу книгу и не буду читать ее, если вы не проверяете правописание в Word и не показываете мне флажок Word, отображаемый первым.
Некоторые мои коллеги, которые участвовали в проекте «оптимизации» существующих пакетов на стороне сервера (написанных на C++), «оптимизировали» до смерти класс ведения журнала (!), Используя специфичный для Win32 код и функции.
Возможно, узкое место было в logger.write (...), кто знает ...
очень возможно, если каждая функция вызывает блокирующий дисковый ввод-вывод при вызове ...
Тогда это проблема во внешнем коде, а не в классе ведения журнала (я не хочу регистрировать каждый байт, который происходит в производственной системе). Пакеты обычно привязаны к вводу-выводу, а не к базе данных.
Один сотрудник должен был проверить доступ к странице для определенной роли - только «Администратор». Вот что она написала:
.
if ( CurrentUser.CurrentRole == "Role1" || CurrentUser.CurrentRole == "Role2")
{
// Access denied
}
else
{
// Access granted
}
вместо
if ( !CurrentUser.CurrentRole.equals("Admin") )
{
// access denied
}
Таким образом, всякий раз, когда в систему добавлялась новая роль, она имела доступ ко всем конфиденциальным страницам.
Тот же сотрудник был также объединен для создания и архивной таблицы для всех запросов.
Полагаю, я мог бы предложить эту жемчужину:
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()), но почему этот код работает?
Еще один модный трюк с производительностью :)
if (!loadFromDb().isEmpty) {
resultList = loadFromDb();
// do something with results
}
За небольшую цену дополнительного попадания в базу данных вы сэкономите все это время, выполняя примерно 10 строк кода, которые, вероятно, в любом случае мало что сделают для пустого списка. И такие вещи были разбросаны по всему коду :)
У меня был коллега, который пытался перехитрить оптимизатор нашего компилятора 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%, а иногда и более медленный код.
Если упомянутый модуль вызывается миллионы и миллионы раз за цикл, я бы одобрил эту оптимизацию, пока он прокомментировал ее чертовски.
@Michael: Я бы не стал, если бы измерения не указывали, что это Быстрее, а не только короче.
В большинстве случаев тернарный оператор более доступен для чтения, чем if. Настаивание на утверждениях над выражениями в C является культурной / религиозной догмой, нет - любой объективной практикой. (Лучшее правило: если вложенный троичный элемент слишком длинный для чтения, вам также не следует использовать if.)
Проблема здесь в том, чтобы взять вся функция и заменить его одним оператором, return, тем самым заменяя всю логику всей функции вложенными троичными. Если бы вы это видели, вы бы поняли. Это не религиозная вещь "я ненавижу тернарные операторы". Я не говорю о том, чтобы взять один if в функцию и заменить его тройным. Это нормально и часто более читабельно. Я говорю о замене всего метода из 30+ строк одним оператором return и вложенными тернариями. Никто не считал новый код более читабельным, но один разработчик считал его более быстрым.
такой способ выражения кода более функциональный. одна точка возврата.
Это не совсем соответствует вопросу, но я все равно упомяну это как предостережение. Я работал над распределенным приложением, которое работало медленно, и прилетел в Вашингтон, чтобы присутствовать на встрече, в первую очередь направленной на решение проблемы. Руководитель проекта начал намечать реорганизацию, направленную на устранение задержки. Я добровольно сообщил, что за выходные провел несколько измерений, которые выявили узкое место в одном методе. Оказалось, что при локальном поиске отсутствует запись, из-за чего приложение должно переходить на удаленный сервер при каждой транзакции. Добавив запись обратно в местный магазин, задержка была устранена - проблема решена. Обратите внимание, что реорганизация не решила бы проблему.
В рамках одного старого проекта мы унаследовали некоторых (в остальном превосходных) программистов встроенных систем, которые имели большой опыт работы с Z-8000.
Нашей новой средой был 32-битный Sparc Solaris.
Один из ребят пошел и поменял все целые на короткие, чтобы ускорить наш код, так как захват 16 бит из ОЗУ был быстрее, чем захват 32 бита.
Мне пришлось написать демонстрационную программу, чтобы показать, что захват 32-битных значений в 32-битной системе был быстрее, чем захват 16-битных значений, и объяснить, что для захвата 16-битного значения ЦП должен был сделать 32-битное значение шириной. доступ к памяти, а затем замаскировать или сдвинуть биты, которые не нужны для 16-битного значения.
Эй, где ты учился математике? 2 инструкции с 1 доступом к кеш-памяти / ОЗУ, очевидно, быстрее, чем 1 инструкция с 1 доступом к кеш-памяти / ОЗУ!
@RazorStorm На более поздних машинах, где пропускная способность и кеш более важны, будет наоборот. Битовая маска / сдвиг обходятся дешево, но вы хотите как можно больше уместить в кеше, а также минимизировать пропускную способность.
@Jed полностью с вами в этом. Когда вы делаете ориентированный на данные дизайн для вещей, интенсивно использующих память, таких как процесс DSP, многие 128-битные + аппаратные средства (AVX, GPU ...) будут быстрее работать с массивами shorts.
Многие программисты не знают или не хотят знать SQL, поэтому они находят «уловки», чтобы избежать реального использования SQL, чтобы они могли получить данные в массиве. Массивы делают некоторых людей счастливыми. (Мне нравятся и курсоры, и массивы. Coke и Pepsi.) Я нашел эти два блока кода в коде некоторых объектно-ориентированных программистов, которые жаловались на медленную работу реляционных баз данных. (ответ - не больше памяти или больше процессоров.)
Таблица в этом случае представляет собой огромную таблицу с uniqueid_col - уникальным идентификатором или уникальной строкой.
Загрузите эти данные в arrayX (потому что массивы должны быть быстрее)
Select uniqueid_col, col2, col3
from super_big_tbl
(псевдокод)
Loop
arrayX.next_record
if uniqueid_col = '829-39-3984'
return col2
end if
end loop
(Мой ответ внизу.)
Следующая простая ошибка, которую я тоже видел. Идея в том, что таким образом вы никогда не получите дубликат:
Select uniqueid_col, col2, col3
from super_big_tbl
group by uniqueid_col, col2, col3
having uniqueid_col = '829-39-3984'
Правильный синтаксис должен быть
Select uniqueid_col, col2, col3
from super_big_tbl
where uniqueid_col = '829-39-3984'
Это из тех программистов, которым нужен администратор баз данных, который держал бы их за руку и иногда давал им пощечину.
Я тоже однажды нашел код, использующий «группировать по» и «имеющий» в открытом исходном коде. Код работал быстро, пока не набрал больше нескольких записей, а затем упал. Можно сказать, что он плохо масштабируется.
Масштабируемость - это ошибка, а не функция. Mh.
Я знаю, что очень поздно к этой теме, но недавно увидел это:
bool isFinished = GetIsFinished();
switch (isFinished)
{
case true:
DoFinish();
break;
case false:
DoNextStep();
break;
default:
DoNextStep();
}
Знаете, на случай, если у логического есть какие-то дополнительные значения ...
Верно, ложно, конечно же, FileNotFound
Эй, у вас всегда должно быть значение по умолчанию / case else / etc. Что происходит, когда какой-нибудь умный человек меняет это логическое значение на перечисление, чтобы отразить другой статус, а затем следующий человек добавляет к перечислению и забывает изменить процедуру? Наличие значения по умолчанию, когда вам не нужно, не требует времени на выполнение и очень мало времени на разработку. Отслеживание случайно введенной логической ошибки, которая возникает во время выполнения ... Это требует времени, денег и репутации. Стежок, сделанный вовремя, стоит девяти.
@Oorang ... зачем тебе вообще это переключатель? Это логическое значение - все, что требуется, - это if / else.
@Damovisa фейспалм ну ... ну ладно :) Пропустил :)
Нет ничего лучше старого доброго enBooleanDummy. Yes, No и Undefined - возможные значения, по умолчанию - Да ...
Это было Nullable <Boolean> ... :)
Не совсем преждевременная оптимизация - но определенно ошибочная - это было прочитано на веб-сайте BBC из статьи, посвященной Windows 7.
Mr Curran said that the Microsoft Windows team had been poring over every aspect of the operating system to make improvements. "We were able to shave 400 milliseconds off the shutdown time by slightly trimming the WAV file shutdown music.
Я еще не пробовал Windows 7, поэтому могу ошибаться, но готов поспорить, что есть и другие проблемы, которые более важны, чем время, необходимое для завершения работы. В конце концов, как только я вижу сообщение «Завершение работы Windows», монитор выключается, и я ухожу - какую пользу мне принесут эти 400 миллисекунд?
Вы, вероятно, обнаружите, что другие проблемы не так легко объяснить непрограммистам на веб-сайте BBC.
Вот этот ракурс, который я не рассматривал - может, я начинаю терять цинизм :-)
Эти 400 мс - это 400 мс потребляемой мощности. Вероятно, незначительно, но, возможно, со временем они прибавятся. Тем не менее, я бы не о чем беспокоился.
Я потерял в общей сложности много часов, ожидая выключения виртуальных машин XP, чтобы я мог перейти к следующему. Очень благодарен за более быстрое выключение.
Интересно, что файлы WAV воспроизводятся асинхронно, поэтому до тех пор, пока фанфары выключения короче времени, необходимого для выключения, обрезка файла WAV ничего не дает. И что еще более интересно, если они так сильно оптимизировали завершение работы, почему каждый блок Windows, который я выключаю, нуждается в эонах, пока он действительно не отключится? (За исключением, конечно, большой красной кнопки.)
Если бы только они могли потратить немного усилий, чтобы сократить время, необходимое для включения! Любой энтузиазм, который у меня появляется по прибытии в офис, сильно подавляется бесконечным ожиданием, когда моя машина станет пригодной для использования. :-(
Возможно, это не принесет вам пользы, но может быть, планете. Как дошел до Закса, мощность, необходимая для работы ПК в течение 400 мс, x (каждый из 365 дней в году), x (бог знает, сколько миллионов компьютеров) не является незначительной. Как долго они могли обрезать файл WAV? 10 минут? Я бы сказал, что это хорошее решение.
@UpTheCreek: это хороший момент, который можно экстраполировать на любую из множества операций, которые занимают слишком много времени - например, что, черт возьми, делает Firefox во время запуска ?, сколько циклов процессора (и связанных с ними мощность) тратится на антивирусное сканирование? и т.д, и т.д ...
Большой номер один на все времена, с которым я снова и снова сталкиваюсь в собственном программном обеспечении:
Не использовать возможности СУБД по причинам «переносимости», потому что «мы можем захотеть перейти к другому поставщику позже».
Читай по губам. Для любых внутренних работ: ЭТО НЕ БУДЕТ!
Это случается. MySQL -> postgresql, поэтому мы ничего не терять.
Или postgres / postgis -> sqlite / Spaceite ... Это была заноза в заднице ...
это происходит в тестах JUnit
Я не думаю, что пессимизация бывает редкостью. По моему опыту настройки производительности, большая часть низкой производительности вызвана «хорошей практикой программирования», оправдываемой именем «эффективности». Примеры:
Коллекции карт или "словари"
Обычно в них используется какое-то хеш-кодирование, поэтому они будут иметь производительность O (1), но не будут работать даже при заполнении гораздо большим количеством элементов, чем обычно используется.
Итераторы Это оправдано тем, что они могут быть оптимизированы в эффективный встроенный код, хотя его редко проверяют, чтобы увидеть, действительно ли это так.
Уведомления и обработка событий как способ сохранения согласованности данных Поскольку структура данных редко нормализуется, необходимо управлять несогласованностью, и уведомление является обычным методом, поскольку он якобы решает проблему «немедленно». Однако есть большая разница между оперативностью и эффективностью. Также «свойства», когда они Get или Set, поощряются к проникновению глубоко в структуру данных, чтобы попытаться сохранить ее согласованность. Эти методы «короткого поводка» могут привести к большим потерям вычислений. Методы «длинного поводка», такие как периодическое циклическое прохождение структуры данных для «восстановления», могут быть немного менее «немедленными», но гораздо более эффективными.
У меня есть намеренная ... Однажды я реализовал сортировку с возвратом ... просто как доказательство концепции;)) Само собой разумеется, ее производительность была ужасной.
var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();
Лучшее использование StringBuilder, которое я когда-либо видел.
Поговорим о «непонятной концепции»! Ух ты!
Здорово. «Мой руководитель говорит, что я должен использовать класс StringBuilder, если я хочу объединить строки. Это то, что я делаю. Так что же не так?» Ржу не могу...
Как насчет ПОБИ - пессимизация явно умышленно?
Мой коллега в 90-х устал от того, что генеральный директор пинал ему задницу только потому, что генеральный директор проводил первый день каждого выпуска программного обеспечения ERP (нестандартного), выявляя проблемы производительности в новых функциях. Даже если новые функциональные возможности сокрушили гигабайты и сделали невозможное возможным, он всегда находил какие-то детали или даже, казалось бы, серьезную проблему, над которой можно было ныть. Он считал, что хорошо разбирается в программировании, и получил свое удовольствие, надрывая задницы программистам.
Из-за некомпетентного характера критики (он был генеральным директором, а не айтишником) моему коллеге так и не удалось понять это правильно. Если у вас нет проблемы с производительностью, вы не сможете ее устранить ...
Пока в одном выпуске он не поместил много вызовов функций Delay (200) (это был Delphi) в новый код. Прошло всего 20 минут после запуска, и ему было приказано явиться в офис генерального директора, чтобы лично принести свои запоздалые оскорбления.
Единственным необычным явлением до сих пор было то, что мои коллеги молчали, когда он возвращался, улыбался, шутил, ходил за парой BigMac, в то время как обычно он пинал столы, кричал о генеральном директоре и компании и проводил остаток дня, отвергнув .
Естественно, теперь мой коллега отдыхал один или два дня за своим столом, улучшая свои навыки прицеливания в Quake - затем на второй или третий день он удалил вызовы задержки, восстановил и выпустил «аварийный патч», о котором он распространил слух. что он потратил 2 дня и 1 ночь, чтобы исправить дыры в производительности.
Это был первый (и единственный) раз, когда злой генеральный директор сказал «отличная работа!» ему. Это все, что имеет значение, верно?
Это был настоящий ПОБИ.
Но это также своего рода оптимизация социальных процессов, так что это на 100% нормально.
Я думаю.
Я помню, как кто-то писал о приложении для обработки данных, которое продавалось на разных уровнях, где «Lite» мог обрабатывать только несколько наборов данных в секунду, а версия «superduper» - тысячи. Единственное отличие исходного кода - это Sleep (N).
Блестяще! Я бы порекомендовал этот стандарт в такой ситуации. В начале разработки выделите большой кусок памяти и добавьте несколько вызовов сна, и всякий раз, когда вам нужно повысить производительность, просто отключите их. Это называется чудотворцем;)
К сожалению, исправить Sleeps до NOP очень просто, так что облегченную версию можно очень легко взломать. Этот резерв «оптимизации» может потребовать наличия исполняемого упаковщика, чтобы усложнить отладку и исправление.
На одной из моих первых должностей в качестве полноценного разработчика я взял на себя проект программы, которая испытывала проблемы с масштабированием. Он будет достаточно хорошо работать с небольшими наборами данных, но полностью выйдет из строя при передаче большого количества данных.
По мере того, как я копался, я обнаружил, что исходный программист стремился ускорить процесс за счет распараллеливания анализа - запуска нового потока для каждого дополнительного источника данных. Однако он допустил ошибку в том, что все потоки требовали общего ресурса, на котором они зашли в тупик. Конечно, все преимущества параллелизма исчезли. Более того, из-за сбоя большинства систем запускались более 100 потоков только для того, чтобы заблокировать все, кроме одного. Моя мощная машина разработчика была исключением, поскольку она обработала набор данных из 150 источников примерно за 6 часов.
Чтобы исправить это, я удалил компоненты многопоточности и очистил ввод-вывод. Без каких-либо других изменений время выполнения набора данных из 150 источников упало ниже 10 минут на моей машине и с бесконечности до менее получаса на средней машине компании.
Я просто предотвращаю это в проекте сегодня. Теперь я знаю, что сделал правильный выбор.
Все ограничения внешнего ключа были удалены из базы данных, потому что в противном случае было бы очень много ошибок.
Кто-то в моем отделе однажды написал струнный класс. Интерфейс похож на CString, но без зависимости от Windows.
Одна из «оптимизаций», которую они сделали, заключалась в том, чтобы нет выделял больше памяти, чем необходимо. По-видимому, не понимая, что такие классы, как std::string, выделяют избыточную память, заключаются в том, что последовательность операций += может выполняться за O (n) раз.
Вместо этого каждый звонок += принудительно перераспределяет, что превращает повторяющиеся добавления в O (n²) Алгоритм Шлемиля Художника.
Одна компания, которую я посетил в качестве консультанта много лет назад, написала функцию сортировки, которая выглядела примерно так:
procedure sort(string[] values, string direction)
begin
while not sorted do
begin
for every value in values
begin
if direction = "Ascending" then
begin
... swap values in ascending order
end
else if direction = "Descending" then
begin
... swap values in descending order
end
end;
end;
end;
Это алгоритм пузырьковой сортировки (который сам по себе неэффективен) с сравнение строк для направления во внутреннем цикле! Я с трудом поверил своим глазам и объяснил, что да, я, вероятно, могу сделать здесь пару улучшений скорости (в конце концов, они были моими клиентами, поэтому мне пришлось дипломатично заявить, что это был самый неоптимальный код, который я когда-либо видел :-))
while true; do echo 3 > /proc/sys/vm/drop_caches; sleep 3600; done
Это заставляло ядро тратить время на очистку дискового кеша, и как только это удалось, все работало медленно, пока кеш не был заполнен заново. Вызвано неправильным представлением о том, что кеш-память диска препятствует доступу памяти для использования приложениями.
«Пессимизация» - отличное слово.