Когда оптимизация преждевременна?

Как сказал кнут,

мы должны забывать о небольшой эффективности, скажем, около 97% времени: преждевременная оптимизация-это корень всех зол.

Это то, что часто возникает в ответах переполнения стека на такие вопросы, как "какой наиболее эффективный механизм цикла", "методы оптимизации SQL?"(и так далее). Стандартный ответ на эти вопросы по оптимизации - это профилировать ваш код и посмотреть, не проблема ли это во-первых, и если это не так, значит Ваша новая техника не нужна.

мой вопрос в том, если конкретная техника отличается, но не особенно неясна или запутана, можно ли это действительно считать преждевременной оптимизацией?

вот статья Рэндалла Хайда под названием ошибка преждевременной оптимизации.

20 ответов


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

некоторые идиомы, которые были введены во имя оптимизации стали настолько популярны, что все понимают их!--8--> и ожидается, а не преждевременно. Примеры включают

  • используя арифметика указателя вместо обозначения массива в C, включая использование таких идиом, как

    for (p = q; p < lim; p++)
    
  • перестановка глобальных переменных в локальные переменные в Lua, как в

    local table, io, string, math
        = table, io, string, math
    

за пределами таких идиом,взять ярлыки на свой страх и риск.

все оптимизация преждевременна если

  • программа слишком медленная (многие забывают эту часть).

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

(также допустимо оптимизировать для памяти.)

прямой ответ на вопрос:

  • если ваша" другая " техника делает программу труднее понять, потом это преждевременная оптимизация.

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


IMHO, 90% вашей оптимизации должно происходить на этапе проектирования, на основе полученных текущих и, что более важно, будущих требований. Если вам нужно вытащить профилировщик, потому что ваше приложение не масштабируется до требуемой нагрузки, вы оставили его слишком поздно, и IMO будет тратить много времени и усилий, не исправляя проблему.

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

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


Если вы не профилировали, это преждевременно.


мой вопрос, если конкретный техника отличается, но не особенно неясный или запутанный, можно ли это считать преждевременная оптимизация?

Хм... Таким образом, у вас есть два метода, идентичные по стоимости (одинаковые усилия по использованию, чтению, изменению), и один более эффективный. Нет, использование более эффективного в этом случае не было бы преждевременным.

прерывание написания кода для поиска альтернатив common Программирование конструирует / библиотечные процедуры на случай, если где-то есть более эффективная версия, даже если вы знаете, что относительная скорость того, что вы пишете, никогда не будет иметь значения... это преждевременно.


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

существует разрыв между тем, чтобы сказать это и сделать.

Я сделал много настройки производительности, выжимая большие факторы из хорошо разработанного кода, казалось бы, без преждевременной оптимизации. вот пример.

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

и в учебном материале, где преподаются эти абстрактные концепции дизайна, такие как архитектура, управляемая уведомлением, и скрытие информации, где простая установка логического свойства объекта может иметь неограниченный эффект пульсации действий, какова причина? эффективность.

Итак, это была преждевременная оптимизация или нет?


во-первых, заставить код работать. Во-вторых, проверьте правильность кода. В-третьих, сделайте это быстро.

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


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

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

кнут также говорит, что всегда лучше вместо оптимизации изменять алгоритмы, которые использует ваша программа, подход, который она принимает к проблеме. Например, в то время как небольшая настройка может дать вам 10% увеличение скорости с оптимизация, фундаментальное изменение способа работы вашей программы может сделать ее в 10 раз быстрее.

в ответ на множество других комментариев, размещенных по этому вопросу: выбор алгоритма != оптимизация


с точки зрения базы данных, не рассматривать оптимальный дизайн на этапе проектирования в лучшем случае безрассудно. Базы данных не рефакторинг легко. Как только они плохо разработаны (это то, что дизайн, который не рассматривает оптимизацию, независимо от того, как вы можете попытаться спрятаться за глупостью преждевременной оптимизации), почти никогда не сможет восстановиться из-за того, что база данных слишком проста для работы всей системы. Гораздо дешевле правильно спроектировать, учитывая оптимальный код для ситуации, которую вы ожидаете, чем ждать, пока есть миллион пользователей, и люди кричат, потому что вы использовали курсоры по всему приложению. Другие оптимизации, такие как использование sargeable кода, выбор наилучших возможных индексов и т. д. только имеет смысл делать во время разработки. Есть причина, почему быстро и грязно это называется. Потому что он не может работать хорошо, поэтому не используйте быстроту в качестве замены хорошего кода. Также откровенно, когда вы понимаете настройка производительности в базах данных вы можете написать код, который с большей вероятностью будет работать хорошо в то же время или меньше, чем требуется для написания кода, который не работает хорошо. Не тратя время на изучение того, что хорошо выполняет дизайн базы данных, - это лень разработчиков, а не лучшая практика.


точка Максим, что обычно оптимизация запутанным и сложным. И обычно, вам архитектор / дизайнер / программист / сопровождающий нужен ясный и сжатый код для того, чтобы понять, что происходит.

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


Я пытаюсь оптимизировать только тогда, когда проблема производительности подтверждена.

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

при написании кода (или запроса БД) я стремлюсь напишите "эффективный" код (т. е. код, который выполняет свою предназначенную функцию, быстро и полностью с простейшей логикой.) Обратите внимание, что "эффективный" код не обязательно совпадает с "оптимизированным" кодом. Оптимизация часто вводит дополнительную сложность в код, что увеличивает затраты на разработку и обслуживание этого кода.

мой совет: попробуйте оплатить только стоимость оптимизации, когда вы можете количественно оценить выгоду.


при программировании жизненно важен ряд параметров. Среди них:

  • читабельности
  • ремонтопригодность
  • сложности
  • надежность
  • достоверность
  • производительность
  • время разработки

оптимизация (переход на производительность) часто происходит за счет других параметров и должна быть сбалансирована с "потерей" в этих областях.

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


оптимизация может происходить на разных уровнях детализации, от очень высокого уровня до очень низкого уровня:

  1. начало с хорошей архитектурой, свободным соединением, модульностью, ЕТК.

  2. Выберите правильные структуры данных и алгоритмы для задачи.

  3. оптимизация для памяти, пытаясь вписать больше кода / данных в кэш. Подсистема памяти в 10-100 раз медленнее, чем процессор, и если ваши данные будут диск, это от 1000 до 10 000 раз медленнее. Осторожность в отношении потребления памяти, скорее всего, обеспечит большую выгоду, чем оптимизация отдельных инструкций.

  4. в каждой функции используйте соответствующие операторы управления потоком. (Переместите неизменяемые выражения за пределы тела цикла. Сначала поместите наиболее распространенное значение в переключатель / корпус и т. д.)

  5. в каждом операторе используйте наиболее эффективные выражения, дающие правильный результат. (Умножение против сдвига и т. д.)

NIT-выбор о том, следует ли использовать выражение divide или выражение shift, не обязательно преждевременная оптимизация. Это преждевременно, если вы не оптимизируете сначала архитектуру, структуры данных, алгоритмы, объем памяти и управление потоком.

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

в большинстве случаев, либо:

A) вы можете достичь порога производительности цели, выполнив оптимизацию высокого уровня, поэтому нет необходимости возиться с выражениями.

или

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

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


ответ Нормана превосходен. Так или иначе, вы обычно делаете некоторую "преждевременную оптимизацию", которая на самом деле является лучшей практикой, потому что делать иначе, как известно, совершенно неэффективно.

например, чтобы добавить в список Нормана:

  • использование конкатенации StringBuilder в Java (или C# и т. д.) вместо String + String (в цикле);
  • избегая петли в C, как: for (i = 0; i < strlen(str); i++) (потому что strlen здесь-вызов функции, идущий по строке каждый раз, вызывается на каждом цикле);
  • кажется, в большинстве реализаций JavaScript, это быстрее сделать тоже for (i = 0 l = str.length; i < l; i++) и он все еще читается, так что хорошо.

и так далее. Но такие микрооптимизации никогда не должны происходить за счет читаемости кода.


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

Я думаю, что "преждевременная оптимизация" - это невероятно субъективно.

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

редизайн дороже, чем оптимизация дизайна очевидными способами с самого начала.

очевидно, что некоторые мелочи будут пропущены в первый раз, но это редко ключевые дизайнерские решения.

поэтому: не оптимизируя дизайн, ИМО-это запах кода сам по себе.


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


Я не думаю, что признанные лучшие практики являются преждевременной оптимизацией. Это больше о времени записи на то, что ifs, которые являются потенциальными проблемами производительности в зависимости от сценариев использования. Хороший пример: если вы сжигаете неделю, пытаясь оптимизировать отражение над объектом, прежде чем у вас есть доказательство того, что это узкое место, вы преждевременно оптимизируете.


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


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

[...] опять же, это заметная экономия в общей скорости движения, если, скажем, среднее значение n составляет около 20, и если процедура поиска выполняется о миллион раз в программе. Такая петля оптимизация [с помощью gotos] не трудно учиться и, как у меня есть сказал, что они подходят только в небольшой части программы, но они часто дают существенную экономию. [...]

и продолжает:

общепринятая мудрость, разделяемая многими современными программистами призывает игнорировать эффективность в малых; но я считаю, что это просто реакция на нарушения они видят практикуется глупые программисты, которые не умеют отлаживать и обслуживать их "оптимизированные" программы. В установленных инженерных дисциплинах a 12% улучшение, легко получаемое, никогда не считается предельным; и я считаю, что та же точка зрения должна преобладать в программной инженерии. Из конечно, я бы не стал делать такие оптимизации на одноразовое задание, но когда речь идет о подготовке качественных программ, я не хочу ограничиться инструменты откажите мне в такой эффективности [т. е. goto заявления в этом контексте].

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

нет никаких сомнений в том, что Грааль эффективность ведет к злоупотреблениям. Программисты тратят огромное количество времени на размышления или беспокойство. о, скорость некритических частей своих программ, и эти попытки повышения эффективности на самом деле оказывают сильное негативное воздействие, когда рассматриваются отладка и обслуживание. Мы должны забыл о малом эффективность, скажем, 97% времени; преждевременная оптимизация-это корень всего зла.

... а затем еще немного о важности профилирования инструменты:

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

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

и все же это была цитата в пользу правильно прикладные микро-оптимизации при использовании опытной рукой, держащей профилировщик. Сегодняшний аналогический эквивалент может быть таким: "люди не должны принимать слепые удары по оптимизации своего программного обеспечения, но пользовательские распределители памяти могут иметь огромное значение при применении в ключевых областях для улучшения локальности ссылки", или, "рукописный SIMD-код с использованием SOA rep очень трудно поддерживать, и вы не должны использовать его повсюду, но он может потреблять память намного быстрее, когда применяется соответствующим образом опытной и управляемой рукой."

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

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

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


Как я вижу, если вы оптимизируете что-то, не зная, сколько производительности вы можете получить в другом сценарии, это преждевременная оптимизация. Цель кода должна действительно сделать его легким для человека, чтобы читать.


Как я написал на аналогичный вопрос, правила оптимизации:

1) не оптимизируют

2) (только для экспертов) оптимизируйте позже

когда оптимизация преждевременна? Обычно.

исключение, возможно, в вашем дизайне или в хорошо инкапсулированном коде, который сильно используется. В прошлом я работал над некоторым критическим кодом времени (реализация RSA), где смотрел на ассемблер, который произвел компилятор и удаление одной ненужной инструкции во внутреннем цикле дало 30% ускорение. Но ускорение от использования более сложных алгоритмов было на порядок больше.

еще один вопрос, чтобы спросить себя, когда оптимизация " я делаю эквивалент оптимизации для модема 300 бод здесь?". Другими словами, закон Мура сделает вашу оптимизацию неуместной в ближайшее время. Многие проблемы масштабирования можно решить, просто бросив больше оборудования на проблема.

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

edit: кстати, что касается связанной статьи, я бы поставил под сомнение многие из сделанных предположений. Во-первых, неправда, что Закон Мура перестал работать в 90-х. Во-вторых, не очевидно, что время пользователя более ценно, чем время программиста. Большинство пользователей (мягко говоря) не отчаянно используют каждый цикл процессора, доступный в любом случае, они, вероятно, ждут, что сеть что-то сделает. Плюс есть возможность затрат, когда время программиста отвлекается от реализации чего-то еще, чтобы сбривать несколько миллисекунд от того, что программа делает, пока пользователь находится на телефоне. Ничего более обычно это не оптимизация, а исправление ошибок.