Оптимизация кода: оптимальный способ оптимизации

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

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

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

Не только для того, чтобы выделиться из толпы как лучшего разработчика. Не только для того, чтобы не стать «Дэном» в The Daily WTF , но и потому, что вы считаете, что оптимизация кода — это то, что нужно делать. Вы гордитесь своей работой.

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

Это благородно с твоей стороны, но остановись.

Просто остановись!

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

Как же так? Давайте резервную копию.

Прежде всего, что такое оптимизация кода?

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

На практике мы иногда по умолчанию используем другое определение: писать меньше кода.

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

Игра в гольф с кодом: +197%, Производительность: -398%, Простота: -9999%

Так что оптимизация кода — немного расплывчатый термин. Это еще до того, как мы рассмотрим некоторые другие способы оптимизации кода, которые мы рассмотрим ниже.

Давайте начнем с того, что прислушаемся к советам мудрецов и вместе исследуем знаменитые правила оптимизации кода Джексона:

  1. Не делай этого.
  2. (Только для экспертов!) Пока не делайте этого.

1. Не делайте этого: направьте перфекционизм

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

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

Ждать. Позвольте мне вернуться к этой автокатастрофе метафоры, которую я только что придумал, и объяснить.

Я занимался исследованиями и разработками для интранет-приложения, которое, как я надеялся, когда-нибудь станет полностью интегрированной системой управления для малого бизнеса, в котором я работал. Он будет отслеживать все для них и, в отличие от их тогдашней системы, никогда не потеряет их данные, потому что будет поддерживаться РСУБД, а не ненадежным доморощенным плоским файлом, который использовал другой разработчик. Я с самого начала хотел спроектировать все как можно умнее, потому что у меня был чистый лист. Идеи для этой системы взорвались в моей голове, как фейерверк, и я начал проектировать таблицы — контакты и их многочисленные контекстуальные вариации для CRM, модулей бухгалтерского учета, инвентаризации, закупок, CMS и управления проектами, которые вскоре буду тестировать.

Что все остановилось в плане разработки и производительности из-за… как вы уже догадались, оптимизации.

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

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

Таблицы базы данных: сотрудник, компания, отношение, тип_отношения

О чувак. Я только что чертовски оптимизировал эту гибкость.

На самом деле слишком много. Теперь у меня возникла новая проблема: заданный тип relationship_type , естественно, не имел бы смысла между каждой заданной комбинацией строк. Хотя может иметь смысл, что у person есть отношение employed by к company , это никогда не может быть семантически эквивалентным отношению, скажем, между двумя document .

Хорошо, нет проблем. Мы просто добавим два столбца в relationship_type , указав, к каким таблицам может быть применено это отношение. (Дополнительные баллы, если вы догадались, что я думал о нормализации этого, переместив эти два столбца в новую таблицу, ссылающуюся наlation_type.id , чтобы relationship_type.id , которые могут семантически применяться к более чем одной паре таблиц, не имели дублирующихся имен таблиц. В конце концов, если бы мне нужно было изменить имя таблицы и я забыл обновить его во всех соответствующих строках, это могло бы создать ошибку!Оглядываясь назад, по крайней мере, ошибки давали бы пищу паукам, населяющим мой череп.)

Таблицы базы данных: Relations_type и apply_to, а также запутанные данные двух столбцов отношения_типа, представленные стрелками.

К счастью, я потерял сознание во время бури палочек-подсказок, прежде чем зайти слишком далеко по этому пути. Когда я проснулся, я понял, что мне удалось более или менее заново реализовать внутренние таблицы РСУБД, связанные с внешними ключами, поверх самой себя. Обычно мне нравятся моменты, которые заканчиваются тем, что я самодовольно заявляю, что «я такая мета», но, к сожалению, это был не один из них. Забудьте о невозможности масштабирования — ужасное раздувание этого дизайна сделало серверную часть моего все еще простого приложения, чья БД еще почти не была заполнена какими-либо тестовыми данными, почти непригодной для использования.

Используй внешние ключи, Люк!

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

Этапы оптимизации кода. Архитектура — это первая часть программы, которую необходимо оптимизировать.

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

Правильно: «О».

Двойной фейспалм, когда один фейспалм не помогает

Это стало для меня важным уроком того, как оптимизация может пойти наперекосяк. Мой перфекционизм полностью рухнул: моя сообразительность привела меня к одному из самых объективно неумных решений, которые я когда-либо принимал.

Оптимизируйте свои привычки, а не код

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

  1. Безопасность
  2. Стабильность во время выполнения
  3. Ясность и стиль
  4. Эффективность кодирования
  5. Эффективность теста
  6. Профилирование
  7. Ваш инструментарий/DE
  8. СУХОЙ (не повторяйтесь)

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

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

Например, что касается DRY: на одной работе я унаследовал кодовую базу, которая как минимум на 80% состояла из избыточных операторов, потому что ее автор явно не знал, как и когда писать функцию. Остальные 20% кода были самоподобны до степени смешения.

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

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

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

Остерегайтесь: оптимизация любого конкретного [аспекта] будет происходить за счет других. По крайней мере, это происходит за счет времени.

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

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

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

Выяснилось, после пары часов бесплодной отладки, что на данный момент это невозможно с шаблонизатором в том виде, в каком я его себе представлял. Не было не только идеального решения DRY; вообще не было СУХОГО решения!

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

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

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

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

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

А как насчет второго правила? Когда мы можем оптимизировать?

2. Пока не делайте этого: кто-то уже сделал это

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

Давайте сделаем еще один шаг вперед: пока даже не кодируйте это .

Это само по себе может пахнуть преждевременной оптимизацией, но это важное исключение. Почему? Чтобы избежать страшного NIHS или синдрома «изобретено не здесь», при условии, что ваши приоритеты включают производительность кода и минимизацию времени разработки. В противном случае, если ваши цели полностью ориентированы на обучение, вы можете пропустить следующий раздел.

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

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

(Хорошим противовесом этому общему шаблону является старый Java Calendar API, но с тех пор он был исправлен.)

Проверьте свою стандартную библиотеку, проверьте экосистему вашего фреймворка, проверьте наличие FOSS, которое уже решает вашу проблему

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

Например, недавно я готовился провести анализ стратегий ИИ для настольной игры. Однажды утром я проснулся, понимая, что анализ, который я планировал, можно было бы выполнить на несколько порядков эффективнее, если бы я просто использовал определенную комбинаторную концепцию, которую запомнил. Не будучи заинтересованным в выяснении алгоритма для этой концепции самостоятельно в это время, я уже был впереди, зная правильное имя для поиска. Однако я обнаружил, что после примерно 50 минут исследований и опробования некоторого предварительного кода мне не удалось превратить найденный наполовину псевдокод в правильную реализацию. (Можете ли вы поверить, что есть сообщение в блоге, в котором автор предполагает неверный вывод алгоритма, неправильно реализует алгоритм, чтобы он соответствовал предположениям, комментаторы указывают на это, а спустя годы это все еще не исправлено?) В этот момент мой утренний чай включился, и я искал [name of concept] [my programming language] . Через 30 секунд у меня был доказуемо правильный код с GitHub, и я переходил к тому, что действительно хотел делать. Просто конкретизация и включение языка вместо того, чтобы предполагать, что мне придется реализовать его самостоятельно, означало все.

Время разработать структуру данных и реализовать алгоритм

…опять же, не играйте в кодовый гольф. Отдайте предпочтение правильности и ясности в реальных проектах.

Затраты времени: 10 часов, Время выполнения: +25%, Использование памяти: +3%, Замешательство: 100%

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

Без проблем. Совет прост, в таком порядке:

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

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

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

Что касается самих тестов, я знаю, что в некоторых кругах разработка через тестирование может вызывать споры. Я думаю, что отчасти причина этого в том, что с этим можно переусердствовать, религиозно заниматься до такой степени, что жертвуют временем разработки. (Опять же мы стреляем себе в ногу, пытаясь с самого начала слишком сильно оптимизировать хотя бы одну переменную.) Даже Кент Бек не доводит TDD до такой крайности, и он изобрел экстремальное программирование и написал книгу по TDD. Поэтому начните с чего-то простого, чтобы убедиться, что ваш вывод правильный. В конце концов, вы все равно будете делать это вручную после написания кода, верно? (Приношу свои извинения, если вы такой крутой программист, что даже не запускаете свой код после того, как написали его впервые. В таком случае, возможно, вы рассмотрите возможность оставить будущим специалистам по сопровождению вашего кода тест, просто чтобы вы знали, что они не будут сломайте вашу замечательную реализацию.) Таким образом, вместо ручного визуального сравнения с готовым тестом вы уже позволяете компьютеру сделать эту работу за вас.

Во время довольно механического процесса реализации ваших алгоритмов и структур данных избегайте построчной оптимизации и даже не думайте об использовании пользовательского языка extern более низкого уровня (Assembly, если вы пишете код на C, C, если вы кодируете на Perl и т. д.) на данный момент. Причина проста: если ваш алгоритм будет полностью заменен — а вы узнаете, нужно ли это, только позже в процессе — тогда ваши усилия по низкоуровневой оптимизации в конечном итоге не дадут никакого эффекта.

Пример ECMAScript

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

 const zeroPhrase = "No more"; const wallPhrase = " on the wall"; const standardizeNumber = number => { if (number === 0) { return zeroPhrase; } return '' + number; } const bottlePhrase = number => { const possibleS = (number === 1) ? '' : 's'; return standardizeNumber(number) + " bottle" + possibleS + " of beer"; } export default class Beer { static verse(number) { const nextNumber = (number === 0) ? 99 : (number - 1); const thisBottlePhrase = bottlePhrase(number); const nextBottlePhrase = bottlePhrase(nextNumber); let phrase = thisBottlePhrase + wallPhrase + ", " + thisBottlePhrase.toLowerCase() + ".\n"; if (number === 0) { phrase += "Go to the store and buy some more"; } else { const bottleReference = (number === 1) ? "it" : "one"; phrase += "Take " + bottleReference + " down and pass it around"; } return phrase + ", " + nextBottlePhrase.toLowerCase() + wallPhrase + ".\n"; } static sing(start = 99, end = 0) { return Array.from(Array(start - end + 1).keys()).map(offset => { return this.verse(start - offset); }).join('\n'); } }

Там вообще практически нет дублирования строк! Написав это таким образом, я вручную реализовал форму сжатия текста для песни о пиве (но только для песни о пиве). В чем конкретно была польза? Ну, допустим, вы хотите спеть о том, что пьете пиво из банок, а не из бутылок. Я мог бы добиться этого, изменив один единственный экземпляр bottle на can .

Хороший!

…правильно?

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

Между тем мои переменные впоследствии будут называться странно, а такие вещи, как bottlePhrase , вообще не будут иметь ничего общего с бутылками . Единственный способ избежать этого — точно предвидеть тип изменения, которое будет сделано, и использовать более общий термин, такой как vessel или container вместо bottle в именах моих переменных.

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

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

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

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

Но мы еще не оптимизировали!

Теперь, когда ваш алгоритм реализован и вы доказали правильность его результатов, поздравляем! У вас есть база!

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

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

Закончите работу над своим приложением (или компонентом), если вы еще этого не сделали, установив все базовые показатели алгоритмических тестов по мере продвижения.

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

Может быть, вы обнаружите, что все в порядке.

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

Хорошо, теперь вы можете оптимизировать

Есть только один способ быть объективным. Пришло время использовать пламенные графы и другие инструменты профилирования. Опытные инженеры могут угадывать или не догадываться чаще, чем новички, но суть не в этом: единственный способ узнать наверняка — это профилировать. Это всегда первое, что нужно сделать в процессе оптимизации кода для повышения производительности.

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

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

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

Общие методы

Во-первых, не забывайте оставаться на высоком уровне как можно дольше:

На уровне всего алгоритма одним из методов является снижение силы. Однако в случае сведения циклов к формулам не забывайте оставлять комментарии. Не все знают или помнят все формулы комбинаторики. Кроме того, будьте осторожны с использованием математики: иногда то, что вы считаете снижением силы, в конце концов таковым не является. Например, предположим, что x * (y + z) имеет некоторый четкий алгоритмический смысл. Если ваш мозг в какой-то момент по какой-то причине был обучен автоматически разгруппировывать похожие термины, у вас может возникнуть соблазн переписать это как x * y + x * z . Во-первых, это ставит барьер между читателем и ясным алгоритмическим смыслом, который там был. (Что еще хуже, теперь это на самом деле менее эффективно из-за необходимости дополнительной операции умножения. Это похоже на раскручивание цикла, которое только что испачкало штаны.) В любом случае, быстрое замечание о ваших намерениях будет иметь большое значение и может даже помочь вам увидеть свои собственную ошибку, прежде чем совершить ее.

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

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

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

Микрооптимизации

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

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

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

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

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

  • Кэширование
  • Битовые хаки и те, что специфичны для 64-битных сред
  • Оптимизация цикла
  • Оптимизация иерархии памяти

Более конкретно:

  • Советы по оптимизации кода на C и C++
  • Советы по оптимизации кода в Java
  • Оптимизация использования ЦП в .NET
  • Кэширование веб-фермы ASP.NET
  • Настройка базы данных SQL или настройка Microsoft SQL Server, в частности
  • Масштабирование Scala Play! фреймворк
  • Расширенная оптимизация производительности WordPress
  • Оптимизация кода с помощью прототипа JavaScript и цепочек областей видимости
  • Оптимизация производительности React
  • Эффективность анимации iOS
  • Советы по производительности Android

В любом случае, у меня есть для вас еще кое-что, чего нельзя делать :

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

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

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

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

Это ваше: оптимально оптимизированная оптимизация

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

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