Можно ли заменить оптимизированный код читаемым кодом?

78

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

Это хорошая идея, чтобы заменить его современным кодом?

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

Компиляторы, похоже, тоже становятся лучше, поэтому такие вещи, как struct abc = {}молчаливое превращение в memsets, shared_ptrв значительной степени производят тот же код, что и необработанный твилинг указателей, шаблоны работают очень хорошо, потому что они генерируют супер обедненный код и так далее.

Но, тем не менее, иногда вы видите стековые массивы и старые функции C с некоторой непонятной логикой, и обычно они не находятся на критическом пути.

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

кодер
источник
20
Читаемость и оптимизация не противоречат большую часть времени.
Deadalnix
23
Можно ли улучшить читаемость с некоторыми комментариями?
один пользователь
17
Беспокоит, что ООП-индикация считается «современным кодом»
Джеймс
7
как философия slackware: если она не сломана, не чините ее, но у вас есть очень, очень веская причина для этого
osdamv
5
Под оптимизированным кодом вы подразумеваете реальный оптимизированный код или так называемый оптимизированный код?
Ден04

Ответы:

115

Где?

  • На домашней странице веб-сайта масштаба Google это недопустимо. Держите вещи как можно быстрее.

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

В целом, каковы нефункциональные требования к той части кода, над которой вы работаете? Если действие должно быть выполнено менее 900 мс. в данном контексте (машина, нагрузка и т. д.) 80% времени, и фактически он работает менее чем за 200 мс. Конечно, 100% времени делают код более читабельным, даже если это может немного повлиять на производительность. Если, с другой стороны, одно и то же действие никогда не выполнялось менее чем за десять секунд, лучше попытаться выяснить, что не так с производительностью (или с требованиями в первую очередь).

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

оборота МайнМа
источник
47
+1! Если у вас нет номеров, получите несколько номеров. Если у вас нет времени, чтобы получить номера, у вас нет времени, чтобы изменить его.
Такрой
49
Зачастую разработчики «оптимизируют», основываясь на мифах и недоразумениях, например, предполагая, что «C» быстрее, чем «C ++», и избегая функций C ++ из общего ощущения, что вещи быстрее без цифр для их резервного копирования. Напоминает мне о разработчике C, за которым я следовал, который думал, что gotoэто быстрее, чем для циклов. По иронии судьбы, оптимизатор лучше справился с циклами for, поэтому он сделал код медленнее и труднее для чтения.
Gort Робот
6
Вместо того, чтобы добавить другой ответ, я добавила +1 к этому ответу. Если важно понимать фрагменты кода, хорошо прокомментируйте их. Я работал в среде C / C ++ / Assembly с унаследованным кодом десятилетней давности с десятками участников. Если код работает, оставьте его в покое и вернитесь к работе.
Крис К
Вот почему я склонен писать только читаемый код. Производительность может быть достигнута, сокращая несколько горячих точек.
Лука
36

Обычно нет .

Изменение кода может вызвать непредвиденные проблемы в других местах системы (которые иногда могут остаться незамеченными гораздо позже в проекте, если у вас нет надежных модульных и дымовых тестов). Я обычно придерживаюсь менталитета «если не сломано, не чини».

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

Конечно, профиль, профиль, профиль , особенно если это критическая область пути.

Демиан Брехт
источник
2
Да, но вы предполагаете, что оптимизация была необходима. Мы не знаем, всегда знаем, было ли это, и мы, вероятно, хотим определить это в первую очередь.
Хайлем
2
@haylem: Нет, я предполагаю, что код работает как есть. Я также предполагаю, что рефакторинг кода неизбежно вызовет проблемы с зацеплением в других частях системы (если только вы не имеете дело с тривиальным фрагментом кода, который не имеет внешних зависимостей).
Демиан Брехт
В этом ответе есть доля правды, и по иронии судьбы это связано с тем, что проблемы, связанные с подделкой, редко документируются, понимаются, сообщаются или даже привлекают внимание разработчиков. Если разработчики имеют более глубокое понимание проблем, которые произошли в прошлом, они будут знать, что измерять , и будут более уверены в внесении изменений в код.
Rwong
29

Вкратце: это зависит

  • Вы действительно нуждаетесь в использовании или используете свою улучшенную версию?

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

    • Почему?
    • На какую цель вы должны стремиться?

Подробно

Вам понадобятся очищенные, блестящие вещи?

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

В частности, знать это:

Существует такая вещь, как Over-Engineering

Это анти-паттерн, и он имеет встроенные проблемы:

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

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

Вам это не нужно

Принцип YAGNI не является полностью ортогональным по отношению к другой проблеме, но он помогает задать себе вопрос: он вам понадобится?

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

Если это не сломано, не исправить это

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

Для нас естественно хотеть «улучшить» код или даже просто коснуться его, даже неосознанно, когда мы читаем его, чтобы попытаться понять его. Это хорошо, так как это означает, что мы самоуверенны и пытаемся глубже понять внутреннее, но это также связано с нашим уровнем навыков, нашими знаниями (как вы решаете, что лучше или нет? Хорошо, см. Разделы ниже). ...) и все наши предположения о том, что мы думаем, что знаем программное обеспечение ...:

  • на самом деле,
  • на самом деле нужно сделать,
  • в конечном итоге нужно будет сделать,
  • и как хорошо это делает.

Это действительно нужно оптимизировать?

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

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

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

Измерить, измерить, измерить

Как Google люди сейчас, это все о данных! Если вы можете подтвердить это данными, это необходимо.

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

Изменения влияют на многие вещи:

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

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

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


В общем...

Да, но...

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

В идеальном мире это правильное решение:

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

На практике:

  • Вы можете сделать это хуже

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

  • ты не можешь предсказать будущее

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

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

Сделайте это частью процесса

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

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

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

Тестовое покрытие, профилирование и сбор данных - это сложно

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

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

Обзоры кода - это тестирование прихожей команды разработчиков

И я думаю, что последняя часть является ключевой здесь: делать обзоры кода. Вы не будете знать ценность ваших улучшений, если сделаете их соло. Обзоры кода - это наше «тестирование в коридоре»: следуйте версии закона Линуса, разработанной Рэймондом, как для выявления ошибок, так и для выявления чрезмерного проектирования и других анти-шаблонов, а также для обеспечения того, чтобы код соответствовал возможностям вашей команды. Нет смысла иметь «лучший» код, если никто, кроме вас, не может его понять и поддерживать, и это касается как загадочных оптимизаций, так и 6-уровневых архитектурных проектов.

Как заключительные слова, помните:

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

хайлем
источник
«Если это не сломано, не исправляйте» противоречит рефакторингу. Неважно, если что-то работает, если не поддерживается, нужно изменить.
Миямото Акира
@MiyamotoAkira: это вещь с двумя скоростями. Если оно не сломано, но приемлемо и менее вероятно, что оно получит поддержку, было бы приемлемо оставить его в покое, вместо того, чтобы вносить потенциальные новые ошибки или тратить на это время разработки. Все дело в оценке выгоды, как краткосрочной, так и долгосрочной, от рефакторинга. Четкого ответа нет, требуется некоторая оценка.
Хайлем
согласовано. Я предполагаю, что мне не нравится предложение (и философия, лежащая в его основе), потому что я вижу рефакторинг как вариант по умолчанию, и только если кажется, что это займет слишком много времени или будет слишком сложным, тогда будет решено / не следует смирись с этим. Имейте в виду, я был сожжен людьми, которые ничего не меняли, хотя, работая, они явно были неправильным решением, как только вам пришлось их поддерживать или расширять.
Миямото Акира
@MiyamotoAkira: короткие предложения и высказывания мнений не могут выразить многое. Они предназначены для того, чтобы быть лицом к лицу и развиваться на стороне, я думаю. Я сам в значительной степени рассматриваю и касаюсь кода как можно чаще, даже если часто без большой сети безопасности или без особых причин. Если это грязно, вы чистите это. Но, аналогично, я тоже несколько раз обгорел. И все равно обожгется. Пока это не 3-й степени, я не возражаю, так что это всегда были краткосрочные ожоги для долгосрочной выгоды.
Хайлем
8

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

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

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

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

Oleksi
источник
8

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

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

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

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

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

HLGEM
источник
6

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

также убедитесь, что код ведет себя так же с соответствующими тестами

трещоткой урод
источник
5

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

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

scarfridge
источник
4

По сути, вы спрашиваете, стоит ли проводить рефакторинг . Ответ на это, безусловно, да.

Но...

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

Мартин Фаулер написал книгу об этом.

Мэтью Флинн
источник
3

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

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

mjfgates
источник
3

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

#ifdef READABLE_ALT_IMPLEMENTATION

   double x=0;
   for(double n: summands)
     x += n;
   return x;

#else

   auto subsum = [&](int lb, int rb){
          double x=0;
          while(lb<rb)
            x += summands[lb++];
          return x;
        };
   double x_fin=0;
   for(double nsm: par_eval( subsum
                           , partitions(n_threads, 0, summands.size()) ) )
     x_fin += nsm;
   return x_fin;

#endif
leftaroundabout
источник
3

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

  1. Ищите тест производительности и поддерживающую информацию профилирования. Если нет теста производительности, то то, что можно утверждать без доказательств, может быть отклонено без доказательств. Утвердите, что ваш современный код работает быстрее, и удалите старый код. Если кто-то спорит (даже сам), попросите его написать код профилирования, чтобы доказать, что быстрее.
  2. Если код профилирования существует, все равно напишите современный код. Назовите это как-то так <function>_clean(). Затем «состряпайте» свой код против плохого кода. Если ваш код лучше, удалите старый код.
  3. Если старый код работает быстрее, оставьте свой современный код в любом случае. Он служит хорошей документацией для того, что должен делать другой код, и, поскольку код «гонки» существует, вы можете продолжать его выполнять для документирования характеристик производительности и различий между двумя путями. Вы также можете выполнить модульный тест на различия в поведении кода. Важно отметить, что современный код превзойдет «оптимизированный» код за один день, гарантировано. Вы можете удалить плохой код.

QED.

Санни Калси
источник
3

Если бы я мог научить мир одной вещи (о программном обеспечении) до своей смерти, я бы сказал, что «Производительность против Х» - это ложная дилемма.

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

Например, автор этого вопроса столкнулся с сумасшедшим фрагментом кода. Если бы этот человек читал мой код, он бы обнаружил, что сумасшедшая часть длиной 3-4 строки. Он сам по себе в методе, а имя и описание метода указывают, ЧТО делает метод. Метод будет содержать 2-6 строк встроенных комментариев, описывающих, как сумасшедший код получает правильный ответ, несмотря на его сомнительный внешний вид.

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

Джейсон
источник
Во многих случаях основным ключом к эффективности является то, чтобы код выполнял как можно больше работы способами, которые могут быть выполнены эффективно. В .NET меня раздражает то, что нет эффективных механизмов, например, для копирования части одной коллекции в другую. Большинство коллекций хранят большие группы последовательных элементов (если не все) в массивах, поэтому, например, копирование последних 5000 элементов из списка из 50 000 элементов должно быть разложено на несколько операций массового копирования (если не только одну) плюс несколько других шаги, сделанные не более, чем несколько раз каждый.
суперкат
К сожалению, даже в тех случаях, когда такие операции могут выполняться эффективно, часто бывает необходимо, чтобы «громоздкие» циклы выполнялись в течение 5000 итераций (а в некоторых случаях 45 000!). Если операция может быть сведена к таким вещам, как массовые копии массивов, то они могут быть оптимизированы до предельной степени, что дает большую отдачу. Если для каждой итерации цикла необходимо выполнить дюжину шагов, трудно особенно хорошо оптимизировать любой из них.
суперкат
2

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

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

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

FrustratedWithFormsDesigner
источник
1
Один из парней, с которыми я работаю, теперь любит оптимизировать вещи в областях, которые пользователи посещают раз в месяц, если так часто. Это занимает время и нередко вызывает другие проблемы, потому что ему нравится кодировать и фиксировать, и пусть QA или другая нисходящая функция действительно тестирует. Честно говоря, он, как правило, быстр, быстр и точен, но эти «оптимистичные» пенни-анте просто усложняют жизнь остальной части команды, и их постоянная смерть будет хорошей идеей.
Дэйв
@DaveE: Эти оптимизации применяются из-за проблем с реальной производительностью? Или этот разработчик делает это только потому, что может? Я полагаю, если вы знаете, что оптимизация не окажет никакого влияния, вы можете смело заменить их более читабельным кодом, но я бы только доверил это тому, кто является экспертом в системе.
FrustratedWithFormsDesigner
Они сделали, потому что он может. Обычно он экономит некоторые циклы, но когда взаимодействие пользователя с элементом программы занимает некоторое количество секунд (от 15 до 300), экономия времени на одну десятую секунды времени в поисках «эффективности» является глупой. Особенно, когда люди, следующие за ним, должны в реальном времени понять, что он сделал. Это приложение PowerBuilder, изначально созданное 16 лет назад, поэтому, учитывая происхождение вещей, мышление, возможно, понятно, но он отказывается обновить свое мышление в соответствии с текущей реальностью.
Дэйв
@DaveE: Думаю, я больше согласен с парнем, с которым ты работаешь, чем с тобой. Если мне не разрешают исправлять вещи, которые медленны по абсолютно без веской причине, я сойду с ума. Если я вижу строку C ++, в которой неоднократно используется оператор + для сборки строки, или код, который каждый раз открывает цикл и читает / dev / urandom только из-за того, что кто-то забыл установить флаг, тогда я исправляю его. Будучи фанатичным об этом, мне удалось сохранить скорость, когда другие люди позволили бы этому скользить по одной микросекунде за раз.
Zan Lynx
1
Ну, мы должны согласиться не согласиться. Тратить час на то, чтобы что-то сэкономить, чтобы сэкономить доли секунды во время выполнения для функции, которая действительно время от времени выполняется, и оставить код в головокружительной форме для других разработчиков ... не правильно. Если это были функции, которые выполнялись неоднократно в частях приложения с высокой нагрузкой, то отлично. Но это не тот случай, который я описываю. Это действительно бесполезный ввод кода в заблуждение по какой-либо другой причине, кроме как сказать: «Я сделал то, что UserX делает раз в неделю, чуть быстрее». Между тем, у нас есть оплачиваемая работа, которую нужно делать.
Дэйв
2

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

И «худой» нечитаемый код не всегда оптимизирован.

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

Джефф Лангемайер
источник
2

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

Следовательно, код будет оптимизирован + правильное комментирование сделает его читабельным.

ПРИМЕЧАНИЕ. Вы можете сделать оптимизированный код читаемым с помощью правильного комментирования, но нельзя сделать читаемый код оптимизированным.

Logicalj
источник
Я бы устал от такого подхода, так как все, что нужно, - это один человек, который редактирует код, чтобы забыть синхронизировать комментарий. Внезапно каждый последующий обзор уйдет, думая, что он выполняет X, фактически выполняя Y.
John D
2

Вот пример, чтобы увидеть разницу между простым кодом и оптимизированным кодом: https://stackoverflow.com/a/11227902/1396264

к концу ответа он просто заменяет:

if (data[c] >= 128)
    sum += data[c];

с участием:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

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

Это выполняется менее чем за четверть исходного времени (11,54 с против 2,5 с)

оборота vedant1811
источник
1

Главный вопрос здесь: требуется ли оптимизация?

Если это так, вы не можете заменить его более медленным, более читаемым кодом. Вам нужно будет добавить комментарии и т. Д., Чтобы сделать его более читабельным.

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

ОДНАКО - убедитесь, что вы точно знаете, что делает код и как его тщательно протестировать, прежде чем начать что-то менять. Это включает пиковое использование и т. Д. Если вам не нужно составлять набор тестовых примеров и запускать их до и после, у вас нет времени на проведение рефакторинга.

Стефан
источник
1

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

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

Питер Б
источник
0

Читаемость IMHO важнее оптимизированного кода, потому что в большинстве случаев микрооптимизация не стоит.

Статья о бессмысленных микрооптимизациях :

Как и большинство из нас, я устал читать сообщения в блоге о бессмысленных микрооптимизациях, таких как замена печати на echo, ++ $ i на $ i ++ или двойные кавычки на одинарные кавычки. Почему? Потому что 99,999999% времени это неактуально.

«print» использует еще один код операции, чем «echo», потому что он на самом деле что-то возвращает. Мы можем сделать вывод, что эхо быстрее, чем печать. Но один код операции ничего не стоит, на самом деле ничего.

Я попробовал на свежую установку WordPress. Сценарий останавливается до того, как он заканчивается «Ошибка шины» на моем ноутбуке, но количество кодов операций уже превысило 2,3 миллиона. Достаточно сказано.

webvitaly
источник
0

Оптимизация относительна. Например:

Рассмотрим класс с кучей членов BOOL:

// no nitpicking over BOOL vs bool allowed
class Pear {
 ...
 BOOL m_peeled;
 BOOL m_sliced;
 BOOL m_pitted;
 BOOL m_rotten;
 ...
};

Возможно, вы захотите преобразовать поля BOOL в битовые поля:

class Pear {
 ...
 BOOL m_peeled:1;
 BOOL m_sliced:1;
 BOOL m_pitted:1;
 BOOL m_rotten:1;
 ...
};

Поскольку тип BOOL определен как INT (который на платформах Windows представляет собой 32-разрядное целое число со знаком), он занимает шестнадцать байтов и объединяет их в один. Это экономия 93%! Кто может пожаловаться на это?

Это предположение:

Поскольку тип BOOL определен как INT (который на платформах Windows представляет собой 32-разрядное целое число со знаком), он занимает шестнадцать байтов и объединяет их в один. Это экономия 93%! Кто может пожаловаться на это?

приводит к:

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

Что раньше было

 push [ebx+01Ch]      ; m_sliced
 call _Something@4    ; Something(m_sliced);

становится

 mov  ecx, [ebx+01Ch] ; load bitfield value
 shl  ecx, 30         ; put bit at top
 sar  ecx, 31         ; move down and sign extend
 push ecx
 call _Something@4    ; Something(m_sliced);

Версия битового поля больше на девять байтов.

Давайте сядем и сделаем некоторую арифметику. Предположим, что каждое из этих полей с битовым полем доступно в вашем коде шесть раз, три раза для записи и три раза для чтения. Стоимость при увеличении кода составляет примерно 100 байт. Это не будет точно 102 байта, потому что оптимизатор может использовать преимущества значений, уже находящихся в регистрах, для некоторых операций, а дополнительные инструкции могут иметь скрытые затраты с точки зрения снижения гибкости регистра. Фактическая разница может быть больше, она может быть меньше, но для вычисления обратной границы давайте назовем это 100. Между тем, экономия памяти составляла 15 байт на класс. Следовательно, точка безубыточности равна семи. Если ваша программа создает менее семи экземпляров этого класса, тогда стоимость кода превышает экономию данных: ваша оптимизация памяти была де-оптимизацией памяти.

Рекомендации

Пол Суатте
источник