Когда я пишу код, я всегда стараюсь сделать мой код максимально чистым и читабельным.
Время от времени наступает момент, когда вам нужно пересечь черту и перейти от красивого чистого кода к более уродливому коду, чтобы сделать его быстрее.
Когда это нормально, чтобы пересечь эту линию?
performance
optimization
quality
readability
maintainability
Кен Кокрейн
источник
источник
Ответы:
Вы пересекаете черту, когда
Вот реальный пример: экспериментальная система, на которой я работаю, генерировала данные слишком медленно, занимая более 9 часов за цикл и используя только 40% ЦП. Вместо того, чтобы перепутать код, я переместил все временные файлы в файловую систему в памяти. Добавлено 8 новых строк некрасивого кода, и теперь загрузка ЦП превышает 98%. Задача решена; безобразия не требуется.
источник
foo
и переименовываю ееfoo_ref
- обычно она находится вышеfoo
в исходном файле. В моем тестовом жгуте я призываюfoo
иfoo_ref
для проверки и измерения относительной производительности.Это ложная дихотомия. Вы можете сделать код быстрым и простым в обслуживании.
То, как вы это делаете, - пишите это чисто, особенно с максимально простой структурой данных.
Затем вы узнаете, где время истощает (запустив его, после того , как вы его написали, а не раньше), и исправьте их один за другим. (Вот пример.)
Добавлено: мы всегда слышим о компромиссах, верно, таких как компромисс между временем и памятью или компромисс между скоростью и ремонтопригодностью? Хотя такие кривые вполне могут существовать, не следует предполагать, что какая-либо конкретная программа находится на кривой или даже где-то рядом с ней.
Любую программу, находящуюся на кривой, можно легко (отдавая определенному программисту) сделать намного медленнее и гораздо менее поддерживаемой, и тогда она окажется совсем рядом с кривой. В такой программе достаточно места для того, чтобы сделать ее быстрее и удобнее в обслуживании.
По моему опыту, именно здесь начинаются многие программы.
источник
В моем существовании OSS я выполняю большую библиотечную работу, направленную на повышение производительности, которая тесно связана со структурой данных вызывающей стороны (т. Е. Внешней по отношению к библиотеке), при этом (по замыслу) нет мандата на входящие типы. Здесь лучший способ сделать это перманентным - это метапрограммирование, которое (поскольку я нахожусь в .NET-land) означает IL-emit. Это некрасивый, уродливый код, но очень быстрый.
Таким образом, я с радостью принимаю, что библиотечный код может быть «более уродливым», чем код приложения , просто потому, что у него меньше (или, возможно, нет) контроля над входными данными , поэтому необходимо выполнять некоторые задачи с помощью различных механизмов. Или, как я выразился на днях:
Теперь код приложения немного отличается, поскольку именно здесь «обычные» (здравомыслящие) разработчики тратят большую часть своего совместного / профессионального времени; цели и ожидания каждого (ИМО) немного отличаются.
IMO, ответы выше, которые предполагают, что его можно быстро и легко поддерживать, относятся к коду приложения, где разработчик имеет больший контроль над структурами данных и не использует такие инструменты, как метапрограммирование. Тем не менее, существуют разные способы выполнения метапрограммирования, с разными уровнями безумия и разными уровнями накладных расходов. Даже на этой арене вам нужно выбрать подходящий уровень абстракции. Но когда вы активно, позитивно, искренне хотите, чтобы он обрабатывал неожиданные данные абсолютно быстрым способом; это может стать ужасным. Справиться с этим, р
источник
Когда вы профилировали код и убедились, что он действительно вызывает значительное замедление.
источник
Чистый код не обязательно является исключительно быстродействующим кодом. Обычно трудный для чтения код был написан потому, что он был написан быстрее, а не потому, что он выполняется быстрее.
Написание «грязного» кода в попытке ускорить его, возможно, неразумно, поскольку вы точно не знаете, что ваши изменения действительно что-то улучшают. Кнут говорит:
Другими словами, сначала напишите чистый код. Затем профилируйте полученную программу и посмотрите, является ли этот сегмент узким местом в производительности. Если это так, оптимизируйте этот раздел по мере необходимости и обязательно включите множество комментариев к документации (возможно, включая исходный код) для объяснения оптимизации. Затем профилируйте результат, чтобы убедиться, что вы действительно внесли улучшения.
источник
Поскольку вопрос говорит «быстро трудно читаемый код», простой ответ никогда не бывает. Нет никакого оправдания написанию кода, который трудно читать. Почему? Две причины.
источник
Когда это одноразовый код. Я имею в виду буквально: когда вы пишете сценарий для выполнения одноразового вычисления или задачи и знаете с такой уверенностью, что вам никогда не придется выполнять это действие снова, что вы можете «rm source-file» без колебаний, тогда вы можете выбрать уродливый маршрут.
В противном случае это ложная дихотомия - если вы думаете, что вам нужно сделать уродливым, чтобы сделать это быстрее, вы делаете это неправильно. (Или ваши принципы о том, что такое хороший код, нуждаются в пересмотре. Использование goto на самом деле довольно элегантно, когда это правильное решение проблемы. Однако редко бывает.)
источник
Всякий раз, когда предполагаемая стоимость более низкой производительности на рынке превышает оценочную стоимость обслуживания кода для рассматриваемого модуля кода.
Люди до сих пор делают скрученные SSE / NEON / и т. Д. сборка, чтобы попытаться побить программное обеспечение конкурента на популярном в этом году чипе процессора.
источник
Не забывайте, что вы можете сделать трудный для чтения код легким для понимания с помощью соответствующей документации и комментариев.
В общем, профиль после того, как вы написали легко читаемый код, который выполняет желаемую функцию. Узкие места могут потребовать от вас сделать что-то, что сделает его более сложным, но вы исправите это, объяснив себя.
источник
Для меня это доля стабильности (как в цементированном бетоне, глине, запеченной в духовке, в камне, написанной перманентными чернилами). Чем более нестабилен ваш код, так как чем выше вероятность того, что вам понадобится изменить его в будущем, тем легче стать гибким, как влажная глина, чтобы оставаться продуктивным. Я также подчеркиваю податливость, а не читабельность. Для меня простота изменения кода важнее, чем простота его чтения. Код может быть легко читаемым и кошмарным для изменения, и какая польза от способности читать и легко понимать детали реализации, если они являются кошмаром для изменения? Если это не просто академическое упражнение, обычно смысл в том, чтобы легко понять код в производственной кодовой базе, состоит в том, чтобы облегчить его изменение по мере необходимости. Если это трудно изменить, тогда много преимуществ читабельности выходят в окно. Читабельность обычно полезна только в контексте гибкости, а гибкость полезна только в контексте нестабильности.
Естественно, что даже самый трудный для поддержки код, который можно себе представить, независимо от того, насколько легко или сложно его читать, не представляет проблемы, если нет причин для его изменения, просто используйте его. И можно добиться такого качества, особенно для низкоуровневого системного кода, где производительность часто имеет наибольшее значение. У меня есть C-код, который я до сих пор регулярно использую, который не менялся с конца 80-х годов. Это не нужно было менять с тех пор. Код бесполезен, написан в битые дни, и я его почти не понимаю. Тем не менее, это все еще применимо сегодня, и мне не нужно понимать его реализацию, чтобы извлечь из него много пользы.
Тщательное написание тестов - один из способов улучшить стабильность. Еще одна развязка. Если ваш код не зависит ни от чего другого, единственная причина, по которой он должен измениться, - это необходимость его изменения. Иногда незначительное количество дублирующегося кода может служить механизмом развязки для существенного улучшения стабильности, что делает его достойным компромиссом, если взамен вы получите код, который теперь полностью независим от всего остального. Теперь этот код неуязвим для изменений во внешнем мире. Между тем код, который зависит от 10 различных внешних библиотек, имеет в 10 раз больше причин для его изменения в будущем.
Еще одна полезная вещь на практике - это отделить вашу библиотеку от нестабильных частей вашей кодовой базы, возможно, даже создавая ее отдельно, как вы могли бы сделать для сторонних библиотек (которые также предназначены для использования, а не изменения, по крайней мере, вашим команда). Именно такая организация может помешать людям вмешиваться в нее.
Еще один минимализм. Чем меньше ваш код пытается сделать, тем более вероятно, что он может делать то, что он делает хорошо. Монолитные конструкции почти постоянно нестабильны, поскольку чем больше и больше функциональных возможностей добавляется к ним, тем более неполными они кажутся.
Стабильность должна быть вашей главной целью всякий раз, когда вы стремитесь написать код, который неизбежно будет трудно изменить, например, распараллеленный код SIMD, который был микротонизирован до смерти. Вы противодействуете сложности обслуживания кода, максимально увеличивая вероятность того, что вам не придется менять код, и, следовательно, вам не придется поддерживать его в будущем. Это сводит затраты на обслуживание к нулю независимо от того, насколько сложен в обслуживании код.
источник