Отвечая на этот вопрос , я начал задаваться вопросом, почему многие разработчики считают, что хороший дизайн не должен учитывать производительность, поскольку это может повлиять на удобочитаемость и / или удобство обслуживания.
Я считаю, что хороший дизайн также учитывает производительность во время его написания, и что хороший разработчик с хорошим дизайном может написать эффективную программу, не оказывая негативного влияния на удобочитаемость или ремонтопригодность.
Хотя я признаю, что существуют крайние случаи, почему многие разработчики настаивают на том, что эффективная программа / дизайн приведет к плохой читабельности и / или плохой ремонтопригодности, и, следовательно, производительность не должна учитываться при проектировании?
Ответы:
Я думаю , что такие взгляды обычно являются реакцией на попытки преждевременной (микро) оптимизации , которая все еще распространена и обычно приносит больше вреда, чем пользы. Когда кто-то пытается противостоять таким взглядам, легко впасть в другую крайность или, по крайней мере, выглядеть так.
Тем не менее верно то, что в последние десятилетия благодаря огромному развитию аппаратных ресурсов для большинства программ, написанных сегодня, производительность перестала быть основным ограничивающим фактором. Конечно, следует принимать во внимание ожидаемую и достижимую производительность на этапе проектирования, чтобы определить случаи, когда производительность может быть (придет) серьезной проблемой . И тогда действительно важно с самого начала создавать дизайн для производительности. Тем не менее, общая простота, удобочитаемость и ремонтопригодность все еще важнее . Как отмечали другие, оптимизированный по производительности код является более сложным, сложным для чтения и обслуживания и более подверженным ошибкам, чем простейшее рабочее решение. Таким образом, любое усилие, потраченное на оптимизацию, должно быть доказано, а не просто верено- приносить реальные выгоды при минимальном ухудшении долгосрочной ремонтопригодности программы. Таким образом, хороший дизайн изолирует сложные части, требующие высокой производительности, от остальной части кода , который поддерживается как можно более простым и чистым.
источник
Если обратиться к вашему вопросу со стороны разработчика, который работает над высокопроизводительным кодом, в дизайне следует учитывать несколько моментов.
Получите это правильно, получите это красивым, получите это быстро. В этой последовательности.
источник
contains
, используйте aHashSet
, а не anArrayList
. Производительность может не иметь значения, но нет причин не делать этого. Используйте соответствие между хорошим дизайном и производительностью - если обрабатываете какую-то коллекцию, попробуйте сделать все за один проход, который, вероятно, будет и более читабельным, и быстрее (вероятно).Если я могу позволить себе «позаимствовать» красивую диаграмму @ greengit и сделать небольшое дополнение:
Нас всех "учили", что есть кривые компромисса. Кроме того, мы все предполагали, что мы такие оптимальные программисты, что любая программа, которую мы пишем, настолько жесткая, что находится на кривой . Если программа находится на кривой, любое улучшение в одном измерении обязательно влечет за собой затраты в другом измерении.
По моему опыту, программы приближаются к любой кривой, будучи настроенными, настроенными, забитыми, обработанными воском и в целом превратившимися в «код-гольф». Большинство программ имеют много возможностей для улучшения во всех измерениях. Вот что я имею в виду.
источник
Именно потому, что высокопроизводительные программные компоненты, как правило, на порядок сложнее, чем другие программные компоненты (при прочих равных условиях).
Даже тогда это не так ясно, если показатели производительности являются критически важным требованием, тогда крайне важно, чтобы проект имел сложность для удовлетворения таких требований. Опасность заключается в том, что разработчик тратит спринт на относительно простую функцию, пытаясь выжать несколько лишних миллисекунд из своего компонента.
Несмотря на это, сложность проекта имеет прямую связь со способностью разработчика быстро изучать и знакомиться с таким проектом, и дальнейшие модификации функциональности в сложном компоненте могут привести к ошибкам, которые могут не быть обнаружены модульными тестами. Сложные проекты имеют гораздо больше аспектов и возможных тестовых случаев, чтобы сделать цель 100% покрытия модульными тестами еще более несбыточной мечтой.
С учетом вышесказанного следует отметить, что неэффективно работающий программный компонент мог работать плохо только потому, что он был глупо написан и излишне сложен из-за невежества оригинального автора (выполнение 8 вызовов базы данных для создания единой сущности, когда только один будет делать это). совершенно ненужный код, который приводит к единственному пути к коду независимо от т. д. и т. д.). Эти случаи больше связаны с улучшением качества кода и увеличением производительности, происходящим как следствие рефакторинга, а НЕ предполагаемым следствием.
Однако, при условии хорошо разработанного компонента, он всегда будет менее сложным, чем аналогично хорошо разработанный компонент, настроенный на производительность (при прочих равных условиях).
источник
Это не так уж много, что эти вещи не могут сосуществовать. Проблема в том, что код каждого медленен, нечитаем и не поддерживается на первой итерации. Остальное время тратится на улучшение всего, что является наиболее важным. Если это производительность, то пойти на это. Не пишите злобно-ужасный код, но если он просто должен быть быстрым X, то сделайте его быстрым X. Я считаю, что производительность и чистота в основном не связаны между собой. Код исполнителя не вызывает уродливого кода. Однако, если вы тратите свое время на настройку каждого фрагмента кода, чтобы быть быстрым, угадайте, чем вы не занимались? Делаем ваш код чистым и понятным.
источник
Как вы видете...
Таким образом, производительность и удобочитаемость тесно связаны между собой - и в большинстве случаев нет больших стимулов отдавать предпочтение первому перед вторым. И я говорю здесь о языках высокого уровня.
источник
По моему мнению, производительность должна быть рассмотрена, когда это актуальная проблема (или, например, требование). Невыполнение этого условия ведет к микрооптимизации, которая может привести к более запутанному коду только для того, чтобы сэкономить несколько микросекунд здесь и там, что, в свою очередь, приводит к менее поддерживаемому и менее читаемому коду. Вместо этого следует сосредоточиться на реальных узких местах системы, если это необходимо , и сделать упор на производительность.
источник
Дело в том, что читабельность не должна всегда превышать эффективность. Если вы с самого начала знаете, что ваш алгоритм должен быть высокоэффективным, то это будет одним из факторов, которые вы используете для его разработки.
Дело в том, что в большинстве случаев не нужно ослеплять быстрый код. Во многих случаях IO или взаимодействие с пользователем вызывает гораздо большую задержку, чем выполнение вашего алгоритма. Дело в том, что вы не должны стараться сделать что-то более эффективным, если не знаете, что это горлышко бутылки.
Оптимизация кода для повышения производительности часто усложняет его, потому что, как правило, он требует разумных действий, а не интуитивно понятного. Более сложный код сложнее поддерживать, а другим разработчикам труднее его освоить (и то, и другое необходимо учитывать). В то же время, компиляторы очень хороши в оптимизации общих случаев. Возможно, что ваша попытка улучшить общий случай означает, что компилятор больше не распознает шаблон и, следовательно, не может помочь вам сделать ваш код быстрым. Следует отметить, что это не означает, что вы пишете все, что хотите, не заботясь о производительности. Вы не должны делать ничего, что явно неэффективно.
Дело в том, чтобы не беспокоиться о мелочах, которые могут сделать вещи лучше. Используйте профилировщик и убедитесь, что 1) то, что у вас сейчас есть, является проблемой, и 2) то, что вы изменили, стало улучшением.
источник
Я думаю, что большинство программистов чувствуют это интуитивно просто потому, что в большинстве случаев код производительности - это код, основанный на гораздо большем количестве информации (о контексте, знаниях об оборудовании, глобальной архитектуре), чем любой другой код в приложениях. Большая часть кода будет выражать только некоторые решения конкретных проблем, которые инкапсулированы в некоторых абстракциях модульным способом (например, функции), и это означает ограничение знания контекста только тем, что входит в эту инкапсуляцию (например, параметрами функции).
Когда вы пишете для высокой производительности, после исправления любых алгоритмических оптимизаций, вы попадаете в детали, которые требуют гораздо больше знаний о контексте. Это может естественно ошеломить любого программиста, который не чувствует себя достаточно сфокусированным для этой задачи.
источник
Поскольку стоимость глобального потепления (из-за этих дополнительных циклов ЦП, масштабируемых на сотни миллионов ПК плюс огромные средства для центров обработки данных) и посредственное время автономной работы (на мобильных устройствах пользователя), необходимое для выполнения их плохо оптимизированного кода, редко появляется на большинстве производительность программиста или экспертные оценки.
Это экономический негативный эффект, похожий на форму игнорируемого загрязнения. Таким образом, соотношение затрат и выгод при взгляде на производительность вообще мысленно искажено от реальности.
Разработчики оборудования усердно работали над добавлением функций энергосбережения и масштабирования часов в последние процессоры. Программисты должны позволить аппаратному обеспечению чаще использовать эти возможности, не проверяя каждый доступный тактовый цикл процессора.
ДОБАВЛЕНО: В древние времена стоимость одного компьютера составляла миллионы, поэтому оптимизация времени процессора была очень важной. Затем стоимость разработки и обслуживания кода стала больше, чем стоимость компьютеров, поэтому оптимизация оказалась не в пользу по сравнению с производительностью программистов. Теперь, однако, другая стоимость становится больше, чем стоимость компьютеров, стоимость питания и охлаждения всех этих центров обработки данных в настоящее время становится больше, чем стоимость всех процессоров внутри.
источник
Я думаю, что трудно достичь всех трех. Два, я думаю, могут быть осуществимы. Например, я думаю, что в некоторых случаях возможно достичь эффективности и читабельности, но с микро-настройкой кода может быть затруднено сопровождение. Самый эффективный код на планете, как правило, испытывает недостаток как в удобстве сопровождения, так и в удобочитаемости, что, вероятно, очевидно для большинства, если только вы не из тех, кто понимает ручной векторизованный SoA-код многопоточного SIMD-кода, который Intel пишет со встроенной сборкой, или самый режущий Алгоритмы, используемые в промышленности, с 40-страничными математическими работами, опубликованными всего 2 месяца назад, и 12 библиотеками кода для одной невероятно сложной структуры данных.
Micro-Efficiency
Я бы сказал, что это может противоречить распространенному мнению, что самый умный алгоритмический код зачастую сложнее поддерживать, чем самый простой микро-настраиваемый алгоритм. Эту идею о том, что улучшения масштабируемости дают больший эффект от микротонизированного кода (например, шаблоны доступа с поддержкой кэша, многопоточность, SIMD и т. Д.), Я бы оспорил, по крайней мере, работая в отрасли, наполненной чрезвычайно сложным структуры данных и алгоритмы (индустрия визуальных эффектов), особенно в таких областях, как обработка сеток, потому что эффект может быть большим, но доллар стоит очень дорого, когда вы вводите новые алгоритмы и структуры данных, о которых никто никогда раньше не слышал, поскольку они являются брендом новый. Далее я
Так что идея о том, что алгоритмическая оптимизация всегда превосходит, скажем, оптимизацию, связанную с шаблонами доступа к памяти, всегда была чем-то, с чем я не совсем согласился. Конечно, если вы используете пузырьковую сортировку, никакая микрооптимизация не поможет вам в этом ... но в разумных пределах я не думаю, что это всегда так ясно. И, возможно, алгоритмические оптимизации сложнее поддерживать, чем микрооптимизации. Я бы обнаружил, что гораздо проще поддерживать, скажем, Intel Embree, который использует классический и простой алгоритм BVH и просто настраивает из него дерьмо, чем код Dreamwork OpenVDB для передовых способов алгоритмического ускорения симуляции жидкости. Так что, по крайней мере, в моей отрасли я хотел бы, чтобы больше людей знакомо с микро-оптимизацией компьютерной архитектуры, как Intel, когда они вышли на сцену, в отличие от тысяч и тысяч новых алгоритмов и структур данных. Благодаря эффективной микрооптимизации люди могут найти все меньше и меньше причин для изобретения новых алгоритмов.
Я работал в унаследованной кодовой базе ранее, где почти каждая пользовательская операция имела свою уникальную структуру данных и алгоритм за ней (добавляя до сотен экзотических структур данных). И большинство из них имели очень искаженные рабочие характеристики, будучи очень узко применимыми. Было бы намного проще, если бы система могла вращаться вокруг пары десятков более широко применяемых структур данных, и я думаю, что это могло бы иметь место, если бы они были оптимизированы микрооптимизацией намного лучше. Я упоминаю этот случай, потому что микрооптимизация потенциально может значительно улучшить удобство сопровождения в таком случае, если это означает разницу между сотнями микропессимизированных структур данных, которые даже нельзя безопасно использовать для строгих целей только для чтения, которые включают в себя пропуски кэша и право против
Функциональные языки
Между тем, часть наиболее поддерживаемого кода, с которым я когда-либо сталкивался, была достаточно эффективной, но чрезвычайно трудной для чтения, поскольку они были написаны на функциональных языках. На мой взгляд, удобочитаемость и удобство обслуживания являются противоречивыми идеями.
Действительно трудно сделать код читабельным, обслуживаемым и эффективным одновременно. Как правило, вам нужно немного пойти на компромисс в одном из этих трех, если не на двух, например, ухудшить удобочитаемость для удобства обслуживания или поставить под угрозу удобство обслуживания для повышения эффективности. Обычно это сопровождается ремонтопригодностью, когда вы ищете много других двух.
Удобочитаемость и ремонтопригодность
Как я уже сказал, я считаю, что удобочитаемость и ремонтопригодность не являются гармоничными понятиями. В конце концов, наиболее читаемый код для большинства из нас, смертных, очень интуитивно отображает шаблоны человеческого мышления, а шаблоны человеческого мышления по своей природе подвержены ошибкам: « Если это происходит, делайте это. Если это происходит, делайте это. В противном случае сделайте это. Я что-то забыл! Если эти системы взаимодействуют друг с другом, это должно произойти так, чтобы эта система могла сделать это ... о, подождите, а как насчет этой системы, когда происходит это событие?«Я забыл точную цитату, но кто-то однажды сказал, что если бы Рим был построен как программное обеспечение, то для того, чтобы свалить его, понадобилась бы только посадка птицы на стену. Так обстоит дело с большинством программного обеспечения. Это более хрупко, чем мы часто заботимся Подумайте. Несколько строк, казалось бы, безобидного кода здесь и там могут остановить его до такой степени, что заставят нас пересмотреть весь дизайн, и языки высокого уровня, которые стремятся быть максимально удобочитаемыми, не являются исключением из таких ошибок человеческого дизайна. ,
Чисто функциональные языки примерно настолько близки к неуязвимым, насколько это возможно (даже не близко к неуязвимым, но относительно гораздо ближе, чем большинство). И это отчасти потому, что они не соответствуют интуитивно человеческой мысли. Они не читаются. Они навязывают нам шаблоны мышления, которые заставляют нас решать проблемы с как можно меньшим количеством особых случаев, используя минимально возможный объем знаний и не вызывая побочных эффектов. Они чрезвычайно ортогональны, они позволяют часто изменять и изменять код без неожиданностей, настолько эпических, что мы должны переосмыслить дизайн на чертежной доске, даже до такой степени, чтобы изменить свое мнение об общем дизайне, не переписывая все. Кажется, его легче поддерживать, но код все еще очень трудно читать,
источник
Одна проблема заключается в том, что ограниченное время разработчика означает, что все, что вы стремитесь оптимизировать, отнимает у вас время на другие вопросы.
Это довольно хороший эксперимент, выполненный по этому вопросу в коде Майера. Различным группам разработчиков было предложено оптимизировать скорость, использование памяти, удобочитаемость, надежность и так далее. Выяснилось, что их проекты получили высокие оценки в том, что им было предложено оптимизировать, но ниже во всех остальных качествах.
источник
Потому что опытные программисты узнали, что это правда.
Мы работали с кодом, который скудный и средний и не имеет проблем с производительностью.
Мы работали над большим количеством кода, который для решения проблем производительности ОЧЕНЬ сложен.
Один из непосредственных примеров, который приходит мне в голову, - это то, что мой последний проект включал 8192 таблицы SQL, разделенных вручную. Это было необходимо из-за проблем с производительностью. Настроить выбор из 1 таблицы намного проще, чем выбрать и сохранить 8 192 шарда.
источник
Есть также некоторые известные части высокооптимизированного кода, которые согнут мозги большинства людей, которые поддерживают случай, когда высокооптимизированный код трудно читать и понимать.
Вот самый известный, как мне кажется. Взято с Quake III Arena и приписано Джону Кармаку, хотя я думаю, что было несколько итераций этой функции, и она изначально не была создана им ( разве Википедия не хороша? ).
источник