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

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

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

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

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

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

Ответ 1

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

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

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

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

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

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

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

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

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

(Также возможно оптимизировать память.)

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

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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

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

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

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

Ответ 11

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

  • читабельность
  • ремонтопригодность
  • Сложность
  • Надёжность
  • Корректность
  • Производительность
  • Время разработки
Оптимизация (для производительности) часто приходит за счет других параметров и должна быть сбалансирована с "потерей" в этих областях.

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

Ответ 12

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

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

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

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

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

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

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

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

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

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

или

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

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

Ответ 13

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

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

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

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

Ответ 14

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

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

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

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

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

Поэтому: НЕ оптимизируя дизайн, IMO сам по себе запах кода.

Ответ 15

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

Ответ 16

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

Ответ 17

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

Ответ 18

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

Ответ 19

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

1) Не оптимизируйте

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

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

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

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

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

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

Ответ 20

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

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

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

Традиционная мудрость, которую разделяют многие современные программисты призывает игнорировать эффективность в малом; но я считаю, что это просто чрезмерная реакция на злоупотребления, которые, по их мнению, практикуются pennywise-and-pound-doolish программисты, которые не могут отлаживать или поддерживать их "оптимизированные" программы. В установленных инженерных дисциплинах 12% улучшение, легко полученное, никогда не считается маргинальным; и я полагают, что одна и та же точка зрения должна преобладать в разработке программного обеспечения. из Конечно, я бы не стал делать такую ​​оптимизацию на одной задаче, но когда речь идет о подготовке качественных программ, я не хочу ограничить себя инструментами, которые лишают меня такой эффективности [т.е. gotoв этом контексте].

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

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

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

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

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

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

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

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

Если вы не вписываетесь в категорию "pennywise-and-pound-foolish", то вы не досрочно оптимизируете стандарты Knuth, даже если вы используете goto, чтобы ускорить критическую (что вряд ли поможет многим оптимизаторам сегодня, но если это произойдет, и в действительно критической области, то вы не будете преждевременно оптимизироваться). Если вы на самом деле применяете то, что вы делаете, в областях, которые действительно необходимы, и они действительно извлекают выгоду из этого, тогда вы прекрасно делаете это в глазах Кнута.