Иногда вы сталкиваетесь с ситуацией, когда вам нужно расширить / улучшить какой-то существующий код. Вы видите, что старый код очень скудный, но его также сложно расширять, и на его чтение требуется время.
Это хорошая идея, чтобы заменить его современным кодом?
Некоторое время назад мне понравился бережливый подход, но сейчас мне кажется, что лучше пожертвовать большим количеством оптимизаций в пользу более высоких абстракций, лучших интерфейсов и более читабельного, расширяемого кода.
Компиляторы, похоже, тоже становятся лучше, поэтому такие вещи, как struct abc = {}
молчаливое превращение в memset
s, shared_ptr
в значительной степени производят тот же код, что и необработанный твилинг указателей, шаблоны работают очень хорошо, потому что они генерируют супер обедненный код и так далее.
Но, тем не менее, иногда вы видите стековые массивы и старые функции C с некоторой непонятной логикой, и обычно они не находятся на критическом пути.
Это хорошая идея, чтобы изменить такой код, если вам нужно прикоснуться к небольшой части в любом случае?
Ответы:
Где?
На домашней странице веб-сайта масштаба Google это недопустимо. Держите вещи как можно быстрее.
В части приложения, которая используется одним человеком один раз в год, вполне приемлемо жертвовать производительностью, чтобы улучшить читаемость кода.
В целом, каковы нефункциональные требования к той части кода, над которой вы работаете? Если действие должно быть выполнено менее 900 мс. в данном контексте (машина, нагрузка и т. д.) 80% времени, и фактически он работает менее чем за 200 мс. Конечно, 100% времени делают код более читабельным, даже если это может немного повлиять на производительность. Если, с другой стороны, одно и то же действие никогда не выполнялось менее чем за десять секунд, лучше попытаться выяснить, что не так с производительностью (или с требованиями в первую очередь).
Кроме того, как улучшение читабельности снизит производительность? Зачастую разработчики приспосабливают поведение, близкое к преждевременной оптимизации: они боятся повысить читабельность, полагая, что это резко ухудшит производительность, в то время как более читаемый код будет тратить на несколько микросекунд больше, выполняя то же действие.
источник
goto
это быстрее, чем для циклов. По иронии судьбы, оптимизатор лучше справился с циклами for, поэтому он сделал код медленнее и труднее для чтения.Обычно нет .
Изменение кода может вызвать непредвиденные проблемы в других местах системы (которые иногда могут остаться незамеченными гораздо позже в проекте, если у вас нет надежных модульных и дымовых тестов). Я обычно придерживаюсь менталитета «если не сломано, не чини».
Исключением из этого правила является то, что вы реализуете новую функцию, которая касается этого кода. Если в этот момент это не имеет смысла, и рефакторинг действительно должен иметь место, тогда делайте это до тех пор, пока время рефакторинга (и достаточное количество тестов и буфера для решения проблем с подделкой) учитывается в оценках.
Конечно, профиль, профиль, профиль , особенно если это критическая область пути.
источник
Вкратце: это зависит
Вы действительно нуждаетесь в использовании или используете свою улучшенную версию?
Это действительно нужно оптимизировать?
Подробно
Вам понадобятся очищенные, блестящие вещи?
Здесь есть вещи, которые нужно соблюдать осторожность, и вам нужно определить границу между реальным, измеримым выигрышем и тем, что является вашим личным предпочтением и потенциально вредной привычкой касаться кода, чего не должно быть.
В частности, знать это:
Существует такая вещь, как Over-Engineering
Это анти-паттерн, и он имеет встроенные проблемы:
Некоторые могут также упомянуть принцип KISS в качестве справочного материала, но здесь он противоречит интуиции: оптимизированный способ - простой или чистый, архитектурный? Ответ не обязательно является абсолютным, как объяснено в остальном ниже.
Вам это не нужно
Принцип YAGNI не является полностью ортогональным по отношению к другой проблеме, но он помогает задать себе вопрос: он вам понадобится?
Является ли более сложная архитектура действительно выгодной для вас, кроме того, что она выглядит более удобной в обслуживании?
Если это не сломано, не исправить это
Напишите это на большом плакате и повесьте рядом с экраном, на кухне, на работе или в конференц-зале разработчиков. Конечно, есть много других мантр, которые стоит повторить самим, но этот особенно важен всякий раз, когда вы пытаетесь выполнить «работу по техническому обслуживанию» и чувствуете желание «улучшить» ее.
Для нас естественно хотеть «улучшить» код или даже просто коснуться его, даже неосознанно, когда мы читаем его, чтобы попытаться понять его. Это хорошо, так как это означает, что мы самоуверенны и пытаемся глубже понять внутреннее, но это также связано с нашим уровнем навыков, нашими знаниями (как вы решаете, что лучше или нет? Хорошо, см. Разделы ниже). ...) и все наши предположения о том, что мы думаем, что знаем программное обеспечение ...:
Это действительно нужно оптимизировать?
Все это говорит, почему это было "оптимизировано" в первую очередь? Говорят, что преждевременная оптимизация - это корень всего зла, и если вы видите недокументированный и, казалось бы, оптимизированный код, обычно можно предположить, что он, вероятно, не соответствует Правилам оптимизации, и не нуждался в усилиях по оптимизации, и что это всего лишь горькое желание обычного разработчика. И снова, может быть, это только ты говоришь сейчас.
Если да, то в каких пределах это становится приемлемым? Если в этом есть необходимость, этот предел существует, и он дает вам возможность улучшить положение вещей или создать жесткую линию для принятия решения.
Также остерегайтесь невидимых характеристик. Скорее всего, ваша «расширяемая» версия этого кода также увеличит объем памяти во время выполнения и предоставит еще больший объем статической памяти для исполняемого файла. Сверкающие функции OO поставляются с такими не интуитивными затратами, как они, и могут иметь значение для вашей программы и среды, в которой она должна работать.
Измерить, измерить, измерить
Как Google люди сейчас, это все о данных! Если вы можете подтвердить это данными, это необходимо.
Это не такая старая история, что за каждым 1 долларом, потраченным на разработку, последуют как минимум 1 доллар на тестирование и как минимум 1 доллар на поддержку (но на самом деле это намного больше).
Изменения влияют на многие вещи:
Таким образом, здесь нужно измерять не только потребление аппаратных ресурсов (скорость выполнения или объем памяти), но и потребление ресурсов командой . Оба должны быть предсказаны для определения целевой цели, которые должны быть измерены, учтены и адаптированы на основе развития.
И для вас, менеджера, это означает, что вы должны вписать его в текущий план развития, так что не стесняйтесь связываться с ним и не впадать в яростное кодирование «мальчик-боец» / «подводная лодка» / «черный ход».
В общем...
Да, но...
Не поймите меня неправильно, в общем, я бы высказался за то, что вы предлагаете, и я часто защищаю это. Но вы должны знать о долгосрочной стоимости.
В идеальном мире это правильное решение:
На практике:
Вы можете сделать это хуже
Вам нужно больше глазных яблок, чтобы видеть это, и чем больше вы усложняете, тем больше глазных яблок вам нужно.
ты не можешь предсказать будущее
Вы не можете знать с абсолютной уверенностью, понадобится ли вам это когда-либо, и даже если бы вам понадобились «расширения», которые было бы проще и быстрее реализовать в старой форме, и если бы их нужно было супероптимизировать ,
с точки зрения руководства, это представляет собой огромные затраты без прямой выгоды.
Сделайте это частью процесса
Вы упоминаете здесь, что это довольно небольшое изменение, и вы имеете в виду некоторые конкретные проблемы. Я бы сказал, что в этом случае все нормально, но у большинства из нас есть личные истории небольших изменений, почти хирургических правок, которые в конечном итоге превратились в кошмар обслуживания и почти пропущенные или взорванные сроки, потому что Джо Программист не видел ни одного из-за причин кода и коснулся того, чего не должно было быть.
Если у вас есть процесс для обработки таких решений, вы снимаете с них личное преимущество:
Тестовое покрытие, профилирование и сбор данных - это сложно
Но, конечно, ваш тестовый код и метрики могут страдать от тех же проблем, которые вы пытаетесь избежать для своего реального кода: проверяете ли вы правильные вещи, и являются ли они правильными в будущем, и вы измеряете правильные вещи?
Тем не менее, в целом, чем больше вы тестируете (до определенного предела) и измеряете, тем больше данных вы собираете и тем безопаснее вы. Плохая аналогия: думайте об этом как о вождении (или о жизни в целом): вы можете стать лучшим водителем в мире, если автомобиль сломается, или кто-то решил убить себя, врезаясь в вашу машину сегодня, ваш навыков может быть недостаточно. Есть и экологические вещи, которые могут поразить вас, и человеческие ошибки также имеют значение.
Обзоры кода - это тестирование прихожей команды разработчиков
И я думаю, что последняя часть является ключевой здесь: делать обзоры кода. Вы не будете знать ценность ваших улучшений, если сделаете их соло. Обзоры кода - это наше «тестирование в коридоре»: следуйте версии закона Линуса, разработанной Рэймондом, как для выявления ошибок, так и для выявления чрезмерного проектирования и других анти-шаблонов, а также для обеспечения того, чтобы код соответствовал возможностям вашей команды. Нет смысла иметь «лучший» код, если никто, кроме вас, не может его понять и поддерживать, и это касается как загадочных оптимизаций, так и 6-уровневых архитектурных проектов.
Как заключительные слова, помните:
источник
В общем, вы должны сосредоточиться на удобочитаемости в первую очередь, а производительность намного позже. В большинстве случаев такая оптимизация производительности в любом случае незначительна, но затраты на обслуживание могут быть огромными.
Конечно, все «маленькие» вещи должны быть изменены в пользу ясности, так как, как вы указали, большинство из них все равно будет оптимизировано компилятором.
Что касается более крупных оптимизаций, может быть шанс, что оптимизации на самом деле имеют решающее значение для достижения разумной производительности (хотя это не удивительно часто). Я хотел бы внести ваши изменения, а затем профилировать код до и после изменений. Если новый код имеет существенные проблемы с производительностью, вы всегда можете вернуться к оптимизированной версии, а если нет, то можете просто придерживаться более чистой версии кода.
Измените только одну часть кода за раз и посмотрите, как это влияет на производительность после каждого раунда рефакторинга.
источник
Это зависит от того, почему код был оптимизирован и каков будет эффект от его изменения, а также от влияния кода на общую производительность. Это также должно зависеть от того, есть ли у вас хороший способ загрузить изменения теста.
Вы не должны вносить это изменение без профилирования до и после и, предпочтительно, под нагрузкой, которая похожа на то, что будет видно на производстве. Это означает, что не нужно использовать крошечное подмножество данных на компьютере разработчика или проводить тестирование, когда только один пользователь использует систему.
Если оптимизация была недавней, вы можете поговорить с разработчиком и точно выяснить, в чем заключалась проблема и насколько медленно приложение было до оптимизации. Это может многое сказать вам о том, стоит ли проводить оптимизацию и для каких условий была необходима оптимизация (например, отчет, охватывающий целый год, возможно, не стал медленным до сентября или октября, если вы тестируете свои изменения в феврале медлительность еще может быть неочевидной, а тест недействительным).
Если оптимизация довольно старая, более новые методы могут быть быстрее и удобочитаемее.
В конечном итоге это вопрос к вашему боссу. Требуется много времени для рефакторинга чего-то, что было оптимизировано, и чтобы убедиться, что изменение не повлияло на конечный результат и что оно работает так же хорошо или, по крайней мере, приемлемо по сравнению со старым способом. Он может захотеть, чтобы вы проводили время в других областях, вместо того чтобы выполнять задачу с высоким риском, чтобы сэкономить несколько минут времени на кодирование. Или он может согласиться с тем, что код сложен для понимания и требует частого вмешательства и что теперь доступны лучшие методы.
источник
если профилирование показывает, что оптимизация не нужна (она не находится в критической секции) или даже имеет худшее время выполнения (в результате плохой преждевременной оптимизации), тогда обязательно замените читаемый код, который легче поддерживать
также убедитесь, что код ведет себя так же с соответствующими тестами
источник
Думайте об этом с точки зрения бизнеса. Какова стоимость изменения? Сколько времени вам нужно для внесения изменений и сколько вы сэкономите в долгосрочной перспективе, сделав код более простым для расширения или поддержки? Теперь прикрепите ценник к этому времени и сравните его с деньгами, потерянными из-за снижения производительности. Возможно, вам нужно добавить или обновить сервер, чтобы компенсировать потерю производительности. Возможно, продукт больше не соответствует требованиям и не может быть продан. Может быть, нет потерь. Может быть, изменение повышает надежность и экономит время в другом месте. Теперь примите решение.
Кстати, в некоторых случаях может оказаться возможным сохранить обе версии фрагмента. Вы можете написать тест, генерирующий случайные входные значения, и проверить результаты с другой версией. Используйте «умное» решение, чтобы проверить результат совершенно понятного и заведомо правильного решения и тем самым получить некоторое заверение (но не доказательство), что новое решение эквивалентно старому. Или пойти другим путем и проверить результат хитрого кода с помощью подробного кода и, таким образом, однозначно задокументировать намерение, стоящее за взломом.
источник
По сути, вы спрашиваете, стоит ли проводить рефакторинг . Ответ на это, безусловно, да.
Но...
... вам нужно сделать это осторожно. Для любого рефакторинга кода вам нужны надежные модульные, интеграционные, функциональные тесты и тесты производительности. Вы должны быть уверены, что они действительно тестируют всю необходимую функциональность. Вам нужна возможность запускать их легко и многократно. Как только вы это сделаете, вы сможете заменить компоненты новыми компонентами, содержащими эквивалентную функциональность.
Мартин Фаулер написал книгу об этом.
источник
Вы не должны менять рабочий, производственный код без уважительной причины. «Рефакторинг» не является достаточной причиной, если вы не можете выполнять свою работу без этого рефакторинга. Даже если вы исправляете ошибки в самом сложном коде, вам нужно время, чтобы понять это и внести наименьшее возможное изменение. Если код так сложно понять, вы не сможете понять его полностью, и поэтому любые изменения, которые вы сделаете, будут иметь непредсказуемые побочные эффекты - ошибки, другими словами. Чем больше изменение, тем больше вероятность того, что у вас возникнут проблемы.
Было бы исключение: если бы у непонятного кода был полный набор модульных тестов, вы могли бы его реорганизовать. Поскольку я никогда не видел и не слышал о непонятном коде с полными модульными тестами, вы сначала пишете модульные тесты, получаете согласие необходимых людей, что эти модульные тесты фактически представляют то, что код должен делать, и ТО вносят изменения в код. , Я сделал это один или два раза; это боль в шее, и очень дорогая, но в итоге она дает хорошие результаты.
источник
Если это всего лишь небольшой фрагмент кода, который делает что-то относительно простое и сложное для понимания, я бы сместил «быстрое понимание» в расширенном комментарии и / или неиспользуемой альтернативной реализации, например
источник
Ответ, без потери общности, да. Всегда добавляйте современный код, когда вы видите трудно читаемый код, и в большинстве случаев удаляйте плохой код. Я использую следующий процесс:
<function>_clean()
. Затем «состряпайте» свой код против плохого кода. Если ваш код лучше, удалите старый код.QED.
источник
Если бы я мог научить мир одной вещи (о программном обеспечении) до своей смерти, я бы сказал, что «Производительность против Х» - это ложная дилемма.
Рефакторинг обычно известен как благо для удобочитаемости и надежности, но он также может легко поддерживать оптимизацию. Когда вы рассматриваете улучшение производительности как серию рефакторингов, вы можете соблюдать правило Campsite, а также ускорять работу приложения. На самом деле, по крайней мере, на мой взгляд, вы обязаны делать это с этической точки зрения.
Например, автор этого вопроса столкнулся с сумасшедшим фрагментом кода. Если бы этот человек читал мой код, он бы обнаружил, что сумасшедшая часть длиной 3-4 строки. Он сам по себе в методе, а имя и описание метода указывают, ЧТО делает метод. Метод будет содержать 2-6 строк встроенных комментариев, описывающих, как сумасшедший код получает правильный ответ, несмотря на его сомнительный внешний вид.
Таким образом, вы можете менять реализации этого метода по своему усмотрению. Действительно, именно так я и написал сумасшедшую версию для начала. Вы можете попробовать или хотя бы спросить об альтернативах. В большинстве случаев вы обнаружите, что наивная реализация заметно хуже (обычно я беспокоюсь только об улучшении в 2-10 раз), но компиляторы и библиотеки постоянно меняются, и кто знает, что вы можете найти сегодня, чего не было, когда функция была написана?
источник
Возможно, не стоит его трогать - если код был написан таким образом по соображениям производительности, это означает, что его изменение может вернуть проблемы с производительностью, которые были ранее решены.
Если вы действительно решили изменить положение вещей , чтобы быть более читаемым и расширяемый: Перед тем, как внести изменения, бенчмарк старый код под тяжелой нагрузкой. Еще лучше, если вы можете найти старый документ или заявку на устранение неполадок, описывающую проблему производительности, которую этот странно выглядящий код должен исправить. Затем, после внесения изменений, снова запустите тесты производительности. Если он не очень отличается или все еще находится в приемлемых параметрах, то, вероятно, все в порядке.
Иногда может случиться так, что когда изменяются другие части системы, этот оптимизированный по производительности код больше не нуждается в такой серьезной оптимизации, но невозможно точно знать это без тщательного тестирования.
источник
Проблема здесь в том, что мы отличаем «оптимизированный» от читабельного и расширяемого. То, что мы, пользователи, видим как оптимизированный код, и то, что компилятор видит как оптимизированный, - это две разные вещи. Код, который вы смотрите на изменение, может вовсе не быть узким местом, и поэтому даже если код «худой», его даже не нужно «оптимизировать». Или, если код достаточно старый, компилятор может оптимизировать встроенные функции, что делает использование более новой простой встроенной структуры одинаково или более эффективным, чем старый код.
И «худой» нечитаемый код не всегда оптимизирован.
Раньше у меня складывалось мнение, что умный / скудный код - это хороший код, но иногда использование неясных правил языка скорее причиняет боль, нежели помогает в создании кода, я был прикушен чаще, чем не при любой встроенной работе, когда пытался быть умным, потому что компилятор превращает ваш умный код во что-то совершенно непригодное для встроенного оборудования.
источник
Я никогда не заменю Оптимизированный код на Читаемый код, потому что не могу пойти на компромисс с производительностью, и я решу использовать правильное комментирование в каждом разделе, чтобы каждый мог понять логику, реализованную в этом Оптимизированном разделе, которая решит обе проблемы.
Следовательно, код будет оптимизирован + правильное комментирование сделает его читабельным.
ПРИМЕЧАНИЕ. Вы можете сделать оптимизированный код читаемым с помощью правильного комментирования, но нельзя сделать читаемый код оптимизированным.
источник
Вот пример, чтобы увидеть разницу между простым кодом и оптимизированным кодом: https://stackoverflow.com/a/11227902/1396264
к концу ответа он просто заменяет:
с участием:
Честно говоря, я понятия не имею, чем был заменен оператор if, но, как говорит ответчик, некоторые побитовые операции дают тот же результат (я просто собираюсь поверить ему на слово) .
Это выполняется менее чем за четверть исходного времени (11,54 с против 2,5 с)
источник
Главный вопрос здесь: требуется ли оптимизация?
Если это так, вы не можете заменить его более медленным, более читаемым кодом. Вам нужно будет добавить комментарии и т. Д., Чтобы сделать его более читабельным.
Если код не должен быть оптимизирован, он не должен (до степени, влияющей на читабельность), и вы можете перефакторизовать его, чтобы сделать его более читабельным.
ОДНАКО - убедитесь, что вы точно знаете, что делает код и как его тщательно протестировать, прежде чем начать что-то менять. Это включает пиковое использование и т. Д. Если вам не нужно составлять набор тестовых примеров и запускать их до и после, у вас нет времени на проведение рефакторинга.
источник
Вот как я делаю вещи: сначала я заставляю его работать в читаемом коде, затем оптимизирую его. Я сохраняю первоисточник и документирую свои шаги по оптимизации.
Затем, когда мне нужно добавить функцию, я возвращаюсь к своему читаемому коду, добавляю функцию и выполняю шаги оптимизации, которые я задокументировал. Поскольку вы задокументировали, очень быстро и легко повторно оптимизировать свой код с помощью новой функции.
источник
Читаемость IMHO важнее оптимизированного кода, потому что в большинстве случаев микрооптимизация не стоит.
Статья о бессмысленных микрооптимизациях :
источник
Оптимизация относительна. Например:
Это предположение:
приводит к:
Рекомендации
Анализ рентабельности битовых полей для коллекции логических значений - The Old New Thing
Бит-поле вредности - Hardwarebug
Читаемые и поддерживаемые битовые поля в C | pagetable.com
источник