fedorthinks

Экспресс-курс · No. 24

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

20 понятий · схема у каждого · шпаргалка в конце

§ 01

Первое правило производительности — то, что нарушают все: не доверяй чутью о том, что медленно. Почти вся впустую потраченная оптимизация идёт от пропуска этого одного шага.

Интуиция о медленности обычно неверна

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

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

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

Карта: 20 понятий в семи группах

Карта метро: сначала видишь линии целиком, потом едешь по станциям.

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

MeasureProfilingMeasure–fix–measureSpeed goalsLatencyThroughputBatchingBottleneckBottleneckLimiting stepAmdahl's lawBiggest winsBetter algorithmN+1 / round tripsCachingDoing lessThe tailAverage trapPercentilesTail latencyRestraintPremature optimizationCorrect → clear → fastPerf by designPracticeOptimization loopPerformance target
Семь групп понятий — по ним идут главы курса.

Как читать курс

Механик не меняет детали наугад: сначала приборы, потом одна починка, потом проверка.

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

Главная идея курса: производительность — это не набор трюков, а метод. Измерь → найди узкое место → почини одно → измерь снова → остановись на цели. Всё остальное в курсе — детали этого цикла.

Profiling — найди реальную стоимость

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

Профайлер (profiler) — инструмент, который меряет, где программа на деле проводит время, функция за функцией. Он превращает «думаю, это медленно» в «эта одна функция — 80% времени выполнения». С этим ты чинишь то, что важно, а не то, что вообразил.

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

share of runtime, function by functiongetOrders()80%renderPage()8%parseJSON()5%formatDate()4%auth()3%← real cost← where you guessedmeasurement, not intuition, points at the problem
Профиль показывает долю времени по функциям. Ты подозревал рендер (8%), а 80% съедает один запрос к базе.
ToolProfiling· профилирование, profiler
Что это
Измерение того, где программа на деле тратит время, по функциям.
Как работает

Профайлер (или логи с таймингами, или метрики) записывает время каждой части и показывает доли.

Пример

«getOrders() — 80% времени запроса» вместо «наверное, тормозит рендер».

Подвох

Профиль на игрушечных данных может указать не туда — меряй на реалистичной нагрузке.

Связи
Первый шаг Measure–Fix–Measure; именно он находит Bottleneck.

Measure–Fix–Measure — измерь, почини, измерь снова

Учёный меняет одну переменную, перезапускает опыт и проверяет результат — никогда не предполагая, что изменение помогло, всегда подтверждая.

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

Без второго измерения ты гадаешь, улучшил ли что-то. Именно так «оптимизации» тихо делают всё медленнее. Относись к каждой починке как к гипотезе, которую проверяешь, — та же дисциплина, что эвалы для ИИ или тесты для кода.

Measurefind the slow partChangeone thingMeasure againsame testKeep itfaster ✓Revertsame or slowerevery fix is a hypothesis — confirm it with a second measurement
Измерь → поменяй одно → измерь тем же тестом. Быстрее — оставляешь, нет — откатываешь.
PracticeMeasure–Fix–Measure· измерь, почини, измерь снова
Что это
Каждая починка проверяется вторым измерением до и после.
Как работает

Один и тот же тест до изменения и после; меняешь одну вещь за раз, чтобы знать, что сработало.

Пример

Добавил индекс: запрос был 400 мс, стал 40 мс — оставляешь. Не изменилось — откатываешь.

Подвох

Без второго замера «оптимизация» может тихо замедлить систему, и никто не заметит.

Связи
Ядро Optimization Loop; родственник эвалов и тестов.

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

§ 02

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

Latency — ожидание одной вещи

Сколько занимает один кофе от заказа до чашки — опыт одного клиента, от начала до конца.

Задержка (latency) — сколько занимает одна операция от начала до конца: один запрос, одна загрузка страницы, один запрос к базе. Это то, что отдельный пользователь чувствует: ожидание.

Когда кто-то говорит «сайт медленный», он почти всегда имеет в виду задержку — паузу между действием и ответом. Снижать задержку — значит делать каждую отдельную вещь быстрее.

latency = one operation, start to finishnetworkserver worknetworkrenderclickpage readywhat one user feels: the wait
Задержка — весь путь одного запроса: сеть, работа сервера, сеть обратно, отрисовка.
MetricLatency· задержка, время ответа
Что это
Время одной операции от начала до конца, обычно в миллисекундах.
Как работает

Складывается из всех шагов пути: сеть, работа сервера, база, отрисовка.

Пример
От клика до готовой страницы — 800 мс.
Подвох

Одна цифра задержки — обычно среднее, а оно прячет медленные случаи (глава 5).

Связи
Отдельная цель от Throughput; меряется через Percentiles.

Throughput — сколько обрабатываешь в секунду

Сколько кофе вся кофейня подаёт за час — не сколько занимает один, а общий поток через заведение.

Пропускная способность (throughput) — сколько работы система обрабатывает за единицу времени: запросов в секунду, задач в минуту. Это про общую ёмкость, а не про отдельное ожидание.

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

throughput = how much work per secondSystem1,000 req / srequests inrequests donenot how long one takes — the total flow through the system
Пропускная способность — общий поток через систему: сколько запросов в секунду она успевает обработать.
MetricThroughput· пропускная способность, req/s
Что это
Объём работы за единицу времени: запросов в секунду, задач в минуту.
Как работает

Растёт, когда система обрабатывает больше параллельно или тратит меньше на каждую единицу.

Пример

API держит 1 000 запросов в секунду; ночной конвейер — миллион строк в час.

Подвох

Высокий throughput не значит, что пользователю быстро: каждый может ждать долго.

Связи
Часто растёт за счёт Latency через Batching.

Batching — пропускная способность ценой задержки

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

Задержка и пропускная способность независимы, и оптимизация одной может навредить другой. Батчинг (batching) — группировка работы — часто поднимает пропускную способность, но добавляет задержку: каждый элемент ждёт, пока соберётся пачка.

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

one at a time4 separate trips · each item leaves at onceServerbatched1 trip for all 4 · more per secondServeritems wait for the batchbatching: throughput ↑ · latency ↑
По одному — каждый элемент уходит сразу, но поездок много. Пачкой — одна поездка, больше в секунду, но каждый ждёт пачку.
TechniqueBatching· батчинг, группировка работы
Что это
Собирать много мелких операций в одну большую.
Как работает

Накладные расходы (поездка по сети, запуск задачи) платятся один раз на пачку, а не на элемент.

Пример
Вставить 1 000 строк одним запросом вместо 1 000 отдельных.
Подвох
Каждый элемент ждёт, пока пачка наберётся, — задержка растёт.
Связи

Меняет Latency на Throughput; та же идея, что лечит N+1 Query.

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

§ 03

Время не размазано ровно. Почти всегда одна часть доминирует — и оптимизировать что-то ещё бессмысленно, пока не починишь её.

Bottleneck — одна медленная часть доминирует

Цепь ровно настолько крепка, насколько её слабейшее звено. Усиливать крепкие звенья бесполезно — только слабое решает, выдержит ли она.

В большинстве медленных систем одно узкое место (bottleneck) отвечает за бо́льшую часть времени — один запрос, один медленный сервис, один плохой цикл. Всё остальное уже достаточно быстро.

Поэтому гадать так расточительно: весь навык — найти доминирующую стоимость, а не улучшать быстрые части.

Auth10 msLoad user15 msQuery orders400 msLogic25 msRender50 ms80% of the timetotal: 500 msone step dominates — that is the bottleneckthe other four together are 100 ms
Запрос идёт через пять шагов, но 400 мс из 500 — один запрос к базе. Это и есть узкое место.
ConceptBottleneck· узкое место
Что это
Та одна часть, которая занимает бо́льшую часть общего времени.
Как работает

Профиль показывает доли; обычно одна часть резко больше остальных.

Пример

Страница грузится 500 мс, и 400 из них — один запрос к базе без индекса.

Подвох
Узкое место редко там, где ты его ждёшь, — поэтому сначала профиль.
Связи

Находится через Profiling; почему его чинить — объясняет Amdahl's Law.

Limiting Step — система быстра настолько, насколько её самый медленный шаг

Шоссе свободно, пока не одно перекрытие полосы, где все ползут. Скорость всей поездки задаёт это одно место, а не свободные участки.

Запрос, который проходит десять шагов, ограничен самым медленным из них — это ограничивающий шаг (limiting step). Ускорить девять быстрых шагов — итог почти не сдвинется; темп задаёт медленный. Так что ты целишься именно в него.

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

beforeQuery 400 msRenderfix the query (400 → 40 ms)afterQueryRender500 → 140 msRender is now the slowest stepfix one bottleneck → the next one shows up
Починили запрос: 500 → 140 мс. Теперь самый медленный шаг — рендер, и следующий раунд целится в него.
ConceptLimiting Step· ограничивающий шаг, следующее узкое место
Что это
Шаг, который сейчас задаёт скорость всего пути.
Как работает

Убрал один ограничивающий шаг — ограничивающим становится следующий по медленности.

Пример

Ускорили запрос в 10 раз — теперь рендер в 50 мс стал самым медленным шагом.

Подвох

Узкое место не исчезает навсегда, оно переезжает — поэтому нужна цель, где остановиться.

Связи

Причина, почему Optimization Loop — цикл, а Performance Target — его выход.

Amdahl's Law — оптимизация в другом месте впустую

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

Оптимизируешь часть, которая занимает 2% времени, — лучшее, что выиграешь, это 2%. Найди часть на 80% — и её починка меняет всё. Эту арифметику называют законом Амдала (Amdahl's law): выигрыш ограничен долей времени, которую занимает ускоряемая часть.

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

speed up Render 2× (10% of the time)Query475 ms · −5%speed up Query 2× (80% of the time)QueryRender300 ms · −40%the win is capped by the share of time that part takes
Ускорить рендер вдвое — минус 5%. Ускорить запрос вдвое — минус 40%. Выигрыш ограничен долей части.
LawAmdahl's Law· закон Амдала
Что это

Ускорение части системы ограничено тем, какую долю времени эта часть занимает.

Как работает

Часть на 10% даже при бесконечном ускорении сэкономит максимум 10%.

Пример

Рендер (10%) вдвое быстрее: 500 → 475 мс. Запрос (80%) вдвое быстрее: 500 → 300 мс.

Подвох

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

Связи

Математика за Bottleneck; аргумент против Premature Optimization.

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

§ 04

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

Better Algorithm (Big-O) — лучший алгоритм бьёт более быструю машину

Два маршрута в один город: быстрая машина на длинной извилистой дороге всё равно проигрывает медленной на прямом срезе. Маршрут важнее двигателя.

Крупнейшие выигрыши часто дают лучший алгоритм или структура данных, а не железо и не микроправки. Как растёт время с ростом входа, описывает Big-O. Цикл O(n²), заменённый на поиск O(n) через хеш-таблицу, может превратить минуты в миллисекунды — никакая более быстрая машина так не сможет.

Прежде чем оптимизировать мелочь, спроси, не неверен ли сам подход. Лучшая форма, как показывает курс о структурах данных, — самое дешёвое и самое крупное ускорение из всех.

input size n →timeO(n²) — nested loopO(n) — hash-map lookupminutes → milliseconds
O(n²) взлетает с ростом входа, O(n) растёт ровно. На больших данных это разница между минутами и миллисекундами.
TechniqueBetter Algorithm· Big-O, алгоритмическая сложность
Что это
Сменить подход так, чтобы работа росла медленнее с ростом данных.
Как работает

Big-O показывает рост: O(n²) — вложенный цикл «каждый с каждым», O(n) — один проход.

Пример

Найти общие элементы двух списков: вложенный цикл → множество (hash set) и один проход.

Подвох

На маленьких данных разницы не видно — она взрывается, когда данных становится много.

Связи
Главный выбор Performance by Design; см. курс о структурах данных.

N+1 Query — слишком много круговых поездок

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

Огромная доля медленности — слишком много круговых поездок (round trips): повторные вызовы базы или сервиса, каждый дешёвый сам по себе, но разрушительный в массе.

Классика — запрос N+1 (N+1 query): достать список, а потом сделать ещё по запросу на каждый элемент. Один поход превращается в сотни. Лекарство — доставать оптом, спросить всё разом. Уменьшить число поездок обычно выгоднее, чем ускорять каждую, потому что сама поездка и есть стоимость.

N+1 queriesAppDBlistorder 1order 2order 3…order N1 + N round tripsBulk fetchAppDBall at onceall rows1 round trip
Слева: 1 запрос за список + N запросов по одному. Справа: один запрос за всё сразу.
Anti-patternN+1 Query· проблема N+1, лишние round trips
Что это
Один запрос за список, потом по запросу на каждый элемент.
Как работает

Каждая поездка в базу стоит сети и накладных расходов; N поездок умножают эту цену на N.

Пример

100 заказов → 1 запрос за список + 100 запросов за клиентов. Починка: один запрос с IN (…) или JOIN.

Подвох

ORM прячет это: код выглядит как простой цикл, а под ним — сотни запросов.

Связи
Лечится той же идеей, что Batching; частый Bottleneck.

Caching — не считай одно и то же дважды

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

Кэширование (caching) — запомнить дорогой результат, чтобы не пересчитывать его. Это один из крупнейших рычагов вообще (у него свой курс).

Первый запрос идёт по долгому пути и сохраняет результат; следующие получают его из памяти. Цена — память и риск отдать устаревшие данные.

RequestCacheseen it?Returnfrom memory · fastCompute / DBslow, expensivehitmissremember resultthe second ask is answered without redoing the work
Попадание (hit) — ответ из памяти. Промах (miss) — считаем по-долгому и запоминаем результат на следующий раз.
TechniqueCaching· кэширование
Что это
Хранить результат дорогой работы и отдавать его повторно.
Как работает

Проверяем кэш: есть — отдаём сразу (hit); нет — считаем, сохраняем, отдаём (miss).

Пример
Страница каталога собирается 300 мс; из кэша — за пару миллисекунд.
Подвох

Кэш может отдать устаревшие данные; когда его сбрасывать — отдельная трудная задача.

Связи
Частный случай Doing Less; подробно — в курсе о кэшировании.

Doing Less — самая быстрая работа та, что ты пропустил

Не готовить то, что никто не заказал, и не накрывать стол на двадцать, когда пришли трое.

Ещё одна классика — делать меньше (doing less). Вычисляй вещи лениво (lazy) — только когда они правда нужны. Не загружай данные, которые не используешь. Пропускай работу, результат которой всё равно выбросишь.

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

before: all the workneeded nowunused data ✗not needed yetthrown away ✗after: do lessneeded nowthe fastest work is the work you skipcompute lazily · don't load what you won't use · don't make what you'll discard
Из всей работы реально нужна часть. Остальное — лишние данные, преждевременные вычисления и выброшенные результаты.
TechniqueDoing Less· lazy evaluation, ленивые вычисления
Что это
Убрать работу, а не ускорять её.
Как работает

Ленивые вычисления, загрузка только нужных полей, отказ от результатов, которые не нужны.

Пример

Тянуть из базы 3 нужных поля, а не все 50; считать отчёт, только когда его открыли.

Подвох
Ленивая работа может выстрелить позже, в неудобный момент, — меряй.
Связи
Включает Caching (не повторять) и лечит часть N+1 Query.

Большинство ускорений — несколько классик: лучший алгоритм, меньше круговых поездок (убей N+1), кэширование дорогих результатов и просто меньше работы. Самая быстрая работа — та, что ты пропустил.

§ 05

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

Average — среднее прячет медленные случаи

В комнате девять человек в комфорте и один в огне — средняя температура приятная. Среднее стирает тот случай, который на деле важен.

Сообщать производительность как среднее (average, mean) опасно успокаивает: оно смешивает быстрое большинство с медленным меньшинством в одно счастливое число. Но пользователи не живут в среднем — каждый живёт в своём запросе, и медленные чувствуют каждую миллисекунду.

Среднее время ответа 200 мс может прятать, что один пользователь из двадцати ждёт пять секунд. Среднее говорит «всё в порядке», пока реальная доля пользователей страдает.

average — looks fine1 in 20 waits 5 s20 requests
Девятнадцать быстрых запросов и один на 5 секунд — а линия среднего всё равно выглядит спокойно.
MetricAverage (Mean)· среднее, ловушка среднего
Что это
Сумма времён, делённая на число запросов.
Как работает
Смешивает быстрые и медленные запросы в одно число.
Пример
«Среднее — 200 мс», а каждый двадцатый ждёт 5 секунд.
Подвох

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

Связи
Заменяется на Percentiles; прячет Tail Latency.

Percentiles — p50 и p99 показывают реальный опыт

Отчёт, где написано не только типичное ожидание, но и «самый медленный 1% людей ждал вот столько», — плохие случаи названы, а не усреднены.

Перцентили (percentiles) показывают то, что прячет среднее. p50 (медиана) — типичный случай: половина запросов быстрее. p99 — медленный хвост: 99% быстрее, так что это примерно худший опыт, который встречает реальный пользователь.

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

p50 — half are fasterp99 — 99% are fasterthe slow taillatency →many requests are fast
Распределение времени ответа: p50 — где-то в толпе быстрых, p99 — далеко в медленном хвосте.
MetricPercentiles· перцентили, p50 / p95 / p99
Что это
pN — время, быстрее которого N% запросов. p50 — медиана, p99 — хвост.
Как работает
Сортируешь времена ответа и берёшь значение на нужной позиции.
Пример
p50 = 120 мс (типично), p99 = 2 с (каждый сотый ждёт две секунды).
Подвох

Перцентили нельзя усреднять между серверами — считай их по всем запросам сразу.

Связи
Делают видимой Tail Latency; на них ставят Performance Target.

Tail Latency — то, что пользователи помнят

Один ужасный ужин в ресторане перевешивает в памяти дюжину нормальных — худшие переживания, а не среднее, формируют то, как тебя судят.

Медленные запросы — хвост (tail latency) — непропорционально формируют то, как пользователи воспринимают продукт, потому что плохой опыт запоминается.

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

Pageone user viewPage donewaits for the slowestcall 1 · 30 mscall 2 · 25 mscall 3 · 40 mscall 4 · 2 scall 5 · 35 ms100 calls × 1% slow each → ~63% of pages hit the tail
Страница ждёт самый медленный из своих вызовов. При 100 вызовах и 1% медленных хвост ловят около 63% страниц.
ConceptTail Latency· хвостовая задержка
Что это
Время самых медленных запросов — p99 и дальше.
Как работает

Когда один ответ собирается из многих вызовов, он ждёт самый медленный из них.

Пример

Каждый вызов медленный лишь в 1% случаев, но страница из 100 вызовов попадает в хвост примерно в 63% случаев.

Подвох

Чем больше вызовов в ответе, тем чаще «редкий» хвост становится нормой.

Связи
Видна через Percentiles; прячется в Average.

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

§ 06

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

Premature Optimization — корень всех зол

Укреплять каждую стену дома против землетрясения, которого может и не быть, до того как кто-то вообще въехал, — огромное усилие на проблему, которой у тебя пока нет.

Знаменитая фраза Дональда Кнута — «преждевременная оптимизация (premature optimization) — корень всех зол» — предостерегает от оптимизации до того, как ты знаешь, что́ и нужно ли вообще ускорять.

Оптимизировать код, который не медленный или даже редко запускается, не даёт ничего и стоит немало: сложность, баги и время, украденное у того, что важно. Большинству кода не нужно быть быстрым — ему нужно быть верным и ясным. Работа над скоростью — для частей, которые измерение доказало горячими (hot).

settingsadminexportsignupreportsemailscheckouthot · measured 70%searchbillingprofilelogsimportspeed work only where measurement shows heateverything else: keep it correct and clear
Из двенадцати модулей горячий один — его и ускоряем. Остальные держим верными и ясными.
Anti-patternPremature Optimization· преждевременная оптимизация
Что это
Оптимизировать до того, как измерение показало, что это нужно.
Как работает

Ты платишь ясностью и временем за скорость в месте, где она никому не нужна.

Пример

Неделя на хитрый кэш для страницы настроек, которую открывают раз в месяц.

Подвох

Запутанный «быстрый» код мешает, когда настоящее узкое место всплывает в другом месте.

Связи

Лечится Profiling; объясняется Amdahl's Law; не путать с Performance by Design.

Correct → Clear → Fast — сначала верно и ясно, быстро там, где важно

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

Порядок такой: сделай верным (correct), сделай ясным (clear) и сделай быстрым (fast) только там, где измерение показывает, что это важно.

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

make it work → make it right → make it fast1. Correctdoes the right thing2. Cleareasy to read and change3. Fastonly where measured hotclear code is easier to speed up later — tangled "fast" code is not
Порядок шагов: сначала верно, потом ясно, и только потом быстро — и только там, где измерено.
PrincipleCorrect → Clear → Fast· make it work, make it right, make it fast
Что это
Порядок приоритетов: верность, ясность, и лишь потом скорость.
Как работает

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

Пример

Пишешь простой понятный обработчик; профиль показал горячий цикл — оптимизируешь только его.

Подвох

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

Связи
Противоядие от Premature Optimization.

Performance by Design — но не игнорируй производительность в дизайне

Ты не делаешь сейсмоустойчивым сарай, но и не строишь небоскрёб на песке. Некоторые решения дёшево принять верно рано и разорительно чинить поздно.

Предупреждение — не «никогда не думай о производительности». Выбрать алгоритм O(n²) там, где O(n) был так же лёгок, или структуру, которая не масштабируется, — это ошибка дизайна, за которую дорого заплатишь потом.

Баланс такой: не микрооптимизируй рано, но и не делай архитектурных выборов, которые медленны по дизайну и дороги в откате. Большую форму выбери верно и дёшево заранее; детали оптимизируй, только когда измерение требует.

Decide earlyTune later, if measuredO(n) vs O(n²) algorithmdata structure that scalesarchitecture shapemicro-tweakstuning a hot functionclever speed trickscheap now, ruinous to change lateronly when a profile asks for it
Слева — решения, которые принимают сразу: алгоритм, структура данных, форма архитектуры. Справа — то, что тюнят потом, по профилю.
PrinciplePerformance by Design· производительность в дизайне
Что это

Большие решения о форме системы сразу принимать с оглядкой на скорость.

Как работает

Алгоритм, структура данных и архитектура выбираются рано; микротюнинг — потом, по замерам.

Пример

Сразу выбрать поиск по индексу, а не перебор всей таблицы, — это не преждевременно.

Подвох

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

Связи

Граница Premature Optimization; главный инструмент — Better Algorithm.

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

§ 07

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

Optimization Loop — измерь, почини узкое место, повтори

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

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

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

MeasureFind bottleneckFix that oneMeasure againStop ✓not yettarget met
Измерь → найди узкое место → почини одно → измерь снова. Цель достигнута — стоп, нет — новый круг.
PracticeOptimization Loop· цикл оптимизации
Что это
Повторяющийся цикл: измерить, найти, починить одно, проверить.
Как работает

Каждый круг — один Measure–Fix–Measure против текущего Limiting Step.

Пример

Круг 1: индекс на запрос. Круг 2: рендер стал узким местом — кэш шаблона. Цель достигнута — стоп.

Подвох
Без цели цикл никогда не заканчивается.
Связи
Собирает Profiling, Bottleneck и Performance Target вместе.

Performance Target — поставь цель, потом остановись

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

У производительности нет естественного конца — всегда можно сделать чуть быстрее. Так что поставь цель (performance target) — «страницы быстрее 300 мс на p99» — и остановись, когда достиг.

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

p99 latencyiterations →target: p99 under 300 msstop here ✓diminishing returns
p99 падает с каждым кругом. Как только кривая ушла под цель — стоп; дальше только убывающая отдача.
PracticePerformance Target· цель производительности, бюджет
Что это
Конкретное число «достаточно быстро», привязанное к перцентилю.
Как работает

Цикл оптимизации идёт, пока метрика выше цели, и останавливается, когда ниже.

Пример
«p99 главной страницы — меньше 300 мс».
Подвох
Цель на среднем обманет — ставь её на p99.
Связи
Выход из Optimization Loop; меряется через Percentiles.

Как всё связано

Карта маршрута целиком: видно, откуда начинаешь, через какие станции идёшь и где выходишь.

Понятия курса складываются в одну картину. Профилирование находит узкое место; цель — latency или throughput — говорит, что именно чинить. Узкое место обычно лечится одной из четырёх классик: лучший алгоритм, меньше круговых поездок, кэш или меньше работы. Потом — измерить снова, по перцентилям, против цели, и остановиться. А преждевременная оптимизация — то, что случается, когда пропускаешь измерение.

ProfilingBottleneckLatency / ThroughputfindsgoalBetter algorithmN+1 / round tripsCachingDoing lessmeasure againPercentiles / p99Perf targetOptimization loopset onstopsPremature optimization ✗skip measuring → optimise a guess, add complexity
Профиль → узкое место → одна из классических починок → измерить снова → цель на p99 останавливает цикл. Внизу — что бывает без измерения.
Прежде чем оптимизировать
  • Измерил ли я, куда на деле уходит время, или гадаю?
  • Задержка или пропускная способность — какой «быстро» мне правда нужен?
  • Где узкое место — та одна часть, что доминирует во времени?
  • Это классика — плохой алгоритм, слишком много круговых поездок, нет кэша, лишняя работа?
  • Слежу ли я за p99, а не только за средним, которое прячет медленный хвост?
  • Есть ли цель, к которой я оптимизирую, чтобы знать, когда остановиться?
Признаки, что ты оптимизируешь хорошо
  • Ты измеряешь до того, как что-то трогать, и измеряешь снова после.
  • Ты чинишь узкое место и игнорируешь части, которые уже достаточно быстры.
  • Твои выигрыши идут от алгоритмов, меньшего числа круговых поездок и кэширования, а не от микроправок.
  • Ты судишь скорость по p99, а не по льстивому среднему.
  • Ты оптимизируешь к цели и останавливаешься, держа остальное ясным и верным.

Шпаргалка: все 20 понятий

Измерение

Profilingпрофилирование

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

Measure–Fix–Measureизмерь, почини, измерь

Каждая починка — гипотеза, которую подтверждает второе измерение.

Цели скорости

Latencyзадержка

Время одной операции от начала до конца — то, что чувствует пользователь.

Throughputпропускная способность

Сколько работы система обрабатывает в секунду.

Batchingбатчинг

Группировать работу: пропускная способность растёт, задержка тоже.

Узкое место

Bottleneckузкое место

Одна часть, которая занимает бо́льшую часть времени.

Limiting Stepограничивающий шаг

Система быстра, как её самый медленный шаг; починил — появится следующий.

Amdahl's Lawзакон Амдала

Выигрыш ограничен долей времени ускоряемой части.

Крупнейшие выигрыши

Better AlgorithmBig-O

Сменить подход: O(n²) → O(n) бьёт любое железо.

N+1 Queryлишние round trips

Запрос на каждый элемент списка; лечится выборкой оптом.

Cachingкэширование

Запомнить дорогой результат и не считать его снова.

Doing Lesslazy evaluation

Не делать ненужную работу: лениво, только нужные данные, без выброшенных результатов.

Хвост

Average (Mean)среднее

Одно спокойное число, которое прячет медленные случаи.

Percentilesp50 / p99

p50 — типичный случай, p99 — примерно худший опыт реального пользователя.

Tail Latencyхвостовая задержка

Самые медленные запросы; их помнят, и при многих вызовах их ловит почти каждый.

Сдержанность

Premature Optimizationпреждевременная оптимизация

Ускорять до того, как измерение показало нужду.

Correct → Clear → Fastmake it work, right, fast

Сначала верно, потом ясно, быстро — только в горячих местах.

Performance by Designпроизводительность в дизайне

Большую форму (алгоритм, структуру, архитектуру) выбирай верно сразу.

Практика

Optimization Loopцикл оптимизации

Измерь → почини одно узкое место → измерь снова → повтори.

Performance Targetцель

Число «достаточно быстро» на p99 — где цикл останавливается.

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

Конец экспресс-курса · 7 глав · 20 понятий

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