Performance capstone: чтение профилей, трейсов и стат
Суть Читай реальные артефакты со всего трека — профиль pprof, сниппет hot path, лог N+1-запросов и отчёт по bundle — предскажи доминирующую цену и выбери фикс с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Каждый юнит трека оставляет свой артефакт: профиль, hot path, лог запросов, отчёт по bundle. Сеньорский навык — прочитать артефакт, назвать доминирующую цену и потянуться к правильному слою, а не к самому привычному.
Цель
Отработай сквозной цикл трека на реальных уликах: прочитай артефакт, локализуй цену, которая доминирует, и выбери фикс на том слое, где эта цена живёт — до того как трогать любую ручку.
Читая этот вывод `top -cum`, где реально проводится время и что говорит закон Амдала об оптимизации json.Marshal?
Heads-up 92% — это cumulative — это корневой фрейм, поэтому под ним по определению почти всё. Cumulative наверху не говорит ничего; flat% — там, где работа реально делается, и это cosineSim.
Heads-up Его cumulative-доля ~6%, поэтому закон Амдала ограничивает выигрыш на уровне страницы 6%, как бы быстро ты его ни сделал. Рычаг — в cosineSim с его 69% flat.
Heads-up Один фрейм держит 69% flat. Это хрестоматийная сигнатура одного доминирующего hot path — полная противоположность равномерному размазыванию.
Артефакт 2 — hot path
// app.scoreAll — вызывается раз на поисковый запрос, ~10k кандидатовfunc scoreAll(q []float32, candidates []Doc) []Result { var results []Result // nil slice for _, d := range candidates { v := append([]float32{}, d.Vector...) // свежая копия на кандидата results = append(results, Result{d.ID, cosineSim(q, v)}) } return results}
Викторина
Completed
Учитывая, что Артефакт 1 указал сюда, какой фикс в этом цикле даёт наибольший рычаг, а какой — отвлечение?
Heads-up GOGC меняет, когда запускается GC, а не сколько аллоцирует этот цикл. Профиль показывает, что цена — в самом пути; устрани per-candidate копию и pre-size слайс вместо отсрочки сборки.
Heads-up Параллелизм умножает те же расточительные per-candidate копии по ядрам и добавляет оверхед координации. Сначала убери лишнюю аллокацию; параллелизм рассматривай только если cosineSim действительно CPU-bound.
Heads-up Подача в cosineSim свеже скопированного слайса на кандидата добавляет аллокацию и cache-miss'ы поверх математики. Устранение копии снижает и аллокацию из профиля, и данные, которые cosineSim должен прострелить.
Артефакт 3 — лог запросов
SELECT id, total FROM orders WHERE user_id = $1 -- набор строк, 50 строкSELECT name FROM customers WHERE id = $1 -- params: 11SELECT name FROM customers WHERE id = $1 -- params: 12SELECT name FROM customers WHERE id = $1 -- params: 13... (ещё 47 запросов той же формы) ...-- итого: 51 запрос, 1 request
Викторина
Completed
Этот лог — один request. Назови паттерн и фикс, который убирает его в корне.
Heads-up 51 запрос идут на одном request и переиспользуют соединение; цена — число round-trip'ов, а не размер пула. Больший пул лишь позволит большему числу request'ов гнать тот же расточительный паттерн из 51 запроса параллельно.
Heads-up customers.id — это primary key; lookup'ы уже быстрые. Трата — делать 50 отдельных round-trip'ов вместо одного батч-запроса — индекс этого не меняет.
Heads-up Реплика размазывает те же 50 round-trip'ов на другую машину; структурный N+1 остаётся, и latency 50 последовательных трипов сохраняется. Батчи доступ вместо его релокации.
Этот route на 82 KB превышает бюджет. Какую цену накладывают эти байты и какой trim даёт наибольший рычаг?
Heads-up Передача — меньшая цена; parse/compile/execute на устройстве доминирует в TTI, а gzip не сокращает эту CPU-работу. Фикс — отгружать меньше байтов, а не полагаться на компрессию.
Heads-up Полный `import _ from 'lodash'` (CommonJS) ломает tree-shaking; отгружаются все 72 KB. Надо импортировать конкретные функции или использовать lodash-es / нативные эквиваленты, чтобы байты ушли.
Heads-up Поднять бюджет под распухание — это капитуляция перед шагом enforce. Бюджеты существуют, чтобы заставить trim; замена moment + lodash убирает ~100 KB, не трогая бюджет.
Итог
Четыре артефакта, одна дисциплина. Профиль top -cum отличает корневой фрейм (огромный cumulative, нулевой рычаг) от настоящего хотспота (высокий flat); сниппет hot path показывает трату на аллокацию, которую предсказал профиль; лог запросов вскрывает N+1 по его повторяющейся single-row форме; отчёт по bundle превращает байты в цену CPU устройства. В каждом случае фикс живёт на собственном слое цены — устрани аллокацию, батчи round-trip’ы, обрежь байты — а не на одну ручку в стороне от того, куда указывают улики.