Как избежать проклятия преждевременной оптимизации

Опубликовано: 2022-03-11

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

Какой? Мне? Моя команда?

Это довольно серьезное обвинение. Позволь мне объяснить.

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

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

Преждевременная оптимизация пытается оптимизировать производительность:

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

Я оптимист, Оптимус.

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

Как человек, занимающийся технологиями, вы, вероятно, иногда задаетесь вопросом, как это может быть $year , и все же, несмотря на все наши достижения, $task так раздражающе трудоемок, так или иначе является приемлемым стандартом. Вы хотите быть худым. Эффективный. Потрясающий. Кто-то вроде программистов Rockstar, которых требуют эти объявления о вакансиях, но с лидерскими отбивными. Поэтому, когда ваша команда пишет код, вы поощряете их делать это правильно с первого раза (даже если «правильно» — это весьма относительное понятие в данном случае). Они знают, что это путь Умного Кодировщика, а также путь Тех, Кому не нужно тратить время на рефакторинг позже.

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

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

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

Что оптимизировать: добро пожаловать в искусство

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

Давайте сделаем это еще более двусмысленным! Только сначала.

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

Существует простое правило оптимизации (производительности), которое гласит : «Не делайте этого ». Звучит довольно просто, чтобы строго следовать этому, но не все с этим согласны. Я тоже не совсем с этим согласен. Некоторые люди просто сразу напишут код лучше, чем другие. Будем надеяться, что для любого человека качество кода, который он напишет в совершенно новом проекте, со временем улучшится. Но я знаю, что для многих программистов это будет не так, потому что чем больше они узнают, тем больше у них будет соблазна преждевременной оптимизации.

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

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

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

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

  1. Легче всего объяснить новому сотруднику
  2. Самый опытный разработчик с наибольшей вероятностью пройдет проверку кода.
  3. Самый ремонтопригодный
  4. Самый быстрый, чтобы написать
  5. Самый простой тест
  6. Самый портативный
  7. и т.п.

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

Это искусство. (См. Искусство компьютерного программирования.)

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

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

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

Помните об UX

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

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

На одной работе компания, в которой я работал, использовала корпоративную систему с закрытым исходным кодом, основанную на самоуверенном технологическом стеке. Фактически, это было настолько самоуверенно, что поставщик, который продал его нам, отказался делать настройки пользовательского интерфейса, которые не соответствовали мнению стека, потому что это было очень болезненно для их разработчиков. Я никогда не использовал их стек, поэтому я не осуждаю их за это, но дело в том, что этот компромисс «хорошо для программиста, плохо для пользователя» был настолько громоздким для моих коллег в определенных контекстах, что я перестал написание стороннего дополнения, повторно реализующего эту часть пользовательского интерфейса системы. (Это значительно повысило производительность. Моим коллегам это понравилось! Более десяти лет спустя это все еще экономит всем время и нервы.)

Я не говорю, что мнение само по себе является проблемой; просто слишком многое из этого стало проблемой в нашем случае. В качестве контрпримера, одно из главных преимуществ Ruby on Rails заключается именно в том, что он самоуверен, в интерфейсной экосистеме, где легко получить головокружение от слишком большого количества вариантов. (Дайте мне что-нибудь с мнениями, пока я не разберусь со своим!)

Напротив, у вас может возникнуть соблазн объявить UX королем всего в вашем проекте. Достойная цель, но позвольте мне рассказать вторую историю.

Через несколько лет после успеха вышеупомянутого проекта один из моих коллег пришел ко мне, чтобы попросить оптимизировать UX, автоматизировав определенный запутанный сценарий из реальной жизни, который иногда возникал, чтобы его можно было решить одним щелчком мыши. Я начал анализировать, возможно ли вообще разработать алгоритм, который не будет иметь ложных срабатываний или отрицательных результатов из-за многих и странных крайних случаев сценария. Чем больше я говорил об этом со своим коллегой, тем больше я понимал, что требования просто не окупятся. Сценарий возникал только время от времени — скажем, раз в месяц — и в настоящее время на его решение у одного человека уходит несколько минут. Даже если бы мы могли успешно автоматизировать его без каких-либо ошибок, потребовались бы столетия, чтобы необходимое время разработки и обслуживания окупилось с точки зрения времени, сэкономленного моими коллегами. Людям-угодникам во мне было трудно сказать «нет», но мне пришлось прервать разговор.

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

Что легко и что сложно для компьютеров

Подход, который мне нравится использовать, заключается в том, чтобы профилировать UX так же, как ваши разработчики профилируют свой код. Узнайте у своих пользователей, где они проводят больше всего времени, нажимая или печатая одно и то же снова и снова, и посмотрите, сможете ли вы оптимизировать эти взаимодействия. Может ли ваш код сделать некоторые обоснованные предположения относительно того, что они, скорее всего, будут вводить, и сделать это по умолчанию без ввода? Помимо некоторых запрещенных контекстов (подтверждение EULA без щелчка?), это действительно может повлиять на производительность и счастье ваших пользователей. Если можете, проведите небольшое юзабилити-тестирование коридора. Иногда у вас могут возникнуть проблемы с объяснением того, с чем компьютеру легко помочь, а с чем нет… но в целом это значение, вероятно, будет иметь большое значение для ваших пользователей.

Как избежать преждевременной оптимизации: когда и как оптимизировать

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

Итак, с точки зрения производительности, каким методам оптимизации нужно следовать? Давайте копать.

Это не массовая инициатива, это Triple-Eh

TL;DR: работайте сверху вниз. Оптимизации более высокого уровня можно сделать раньше в проекте, а более низкоуровневые лучше оставить на потом. Это все, что вам нужно, чтобы понять смысл фразы «преждевременная оптимизация»; выполнение действий не в этом порядке с высокой вероятностью приведет к пустой трате времени вашей команды и будет контрэффективным. В конце концов, вы не пишете весь проект в машинном коде с самого начала, не так ли? Таким образом, наш метод работы AAA заключается в оптимизации в следующем порядке:

  1. Архитектура
  2. Алгоритмы
  3. сборка

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

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

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

Архитектура

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

В проекте архитектура — это самая дорогая часть, которую нужно изменить постфактум, поэтому это то место, где имеет смысл оптимизировать в начале. Например, если ваше приложение должно доставлять данные через страусов, вы захотите структурировать его для низкочастотных пакетов с высокой полезной нагрузкой, чтобы не усугубить плохое узкое место. В этом случае вам лучше иметь полную реализацию Tetris, чтобы развлекать ваших пользователей, потому что загрузчик просто не собирается его сокращать. (Шутки в сторону: много лет назад я устанавливал свой первый дистрибутив Linux, Corel Linux 2.0, и был рад, что долгий процесс установки включал именно это. Видя столько раз рекламные экраны установщика Windows 95, что я запомнил их, этот для того времени это был глоток свежего воздуха)

В качестве примера того, что архитектурные изменения являются дорогостоящими, причина того, что вышеупомянутый отчет SQL был настолько крайне немасштабируемым, ясна из его истории. Приложение развивалось с течением времени, начиная с MS-DOS и доморощенной настраиваемой базы данных, которая изначально даже не была многопользовательской. Когда поставщик, наконец, переключился на SQL, схема и код отчетов, похоже, были перенесены один за другим. Это оставило им годы впечатляющих улучшений производительности более чем на 1000%, которые они добавляли в свои обновления всякий раз, когда им удавалось завершить переключение архитектуры, фактически используя преимущества SQL для данного отчета. Хорошо подходит для бизнеса с заблокированными клиентами, такими как мой тогдашний работодатель, и явно пытается расставить приоритеты эффективности кодирования во время начального перехода. Но удовлетворение потребностей клиентов в некоторых случаях так же эффективно, как молоток крутит винт.

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

Я бы так не назвал, но все так делают

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

Идентификацию модных словечек может быть сложно выполнить просто с помощью поиска, потому что для того, что вы называете FLDSMDFR, кто-то уже придумал термин SCOPWWHWTT, и они описывают ту же проблему, которую вы решаете, но используя совершенно другой словарь, чем вы. Сообщества разработчиков спешат на помощь! Напишите на StackExchange или HashNode как можно более подробное описание, а также все модные словечки о том, что ваша архитектура не является , чтобы они знали, что вы провели достаточное предварительное исследование. Кто-нибудь будет рад вас просветить.

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

Алгоритмы и сборка

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

Но как только вы и ваша команда внедрили что-то неоптимизированное с точки зрения производительности, вы действительно оставляете его на «Не делать этого »? Вы никогда не оптимизируете?

Ты прав. Следующее правило, только для экспертов, пока не делайте этого .

Время для эталона!

Ваш код работает. Возможно, он настолько медленный, что вы уже знаете , что его нужно оптимизировать, потому что этот код будет выполняться часто. Может быть, вы не уверены, или у вас есть алгоритм O(n) и вы полагаете, что это, вероятно, нормально. В любом случае, если этот алгоритм когда-либо стоит оптимизировать, моя рекомендация на данный момент одна и та же: запустите простой тест.

Почему? Разве не ясно, что мой алгоритм O(n³) не может быть хуже любого другого? Ну по двум причинам:

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

Не верите мне по второму пункту?

Как получить лучшие результаты от оборудования за 1400 долларов, чем от оборудования за 7000 долларов

Джефф Этвуд из StackOverflow однажды заметил, что иногда (обычно, по его мнению) может быть более рентабельно просто купить лучшее оборудование, чем тратить драгоценное время программиста на оптимизацию. Итак, предположим, вы пришли к достаточно объективному выводу, что ваш проект соответствует этому сценарию. Далее предположим, что вы пытаетесь оптимизировать время компиляции, потому что вы работаете над большим проектом Swift, и это стало довольно большим узким местом разработчика. Время покупки оборудования!

Что вы должны купить? Ну, очевидно, иена за иен, более дорогое оборудование, как правило, работает лучше, чем более дешевое. Таким образом, очевидно, что Mac Pro за 7000 долларов должен компилировать ваше программное обеспечение быстрее, чем какой-нибудь Mac Mini среднего класса, верно?

Неправильно!

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

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

ОК, я уже сделал бенчмаркинг. Могу ли я на самом деле оптимизировать еще ??

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

Подумайте, прежде чем мешать

«Не знаю, как вы, люди, но…»

Гэвин Белсон из Кремниевой долины говорит: «Я не хочу жить в мире, где кто-то другой делает мир лучше… лучше, чем мы».

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

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

Кроме того, если мы этого не сделаем, это сделает кто-то другой.

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

Постскриптум

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