Skip to content
PПромтбук
RUEN
04Производительность

Профайлинг и hot path

Найти где код реально тратит время — без оптимизации вслепую.

Профилируй код прежде чем оптимизировать.

Закон: "Premature optimization is the root of all evil" — пока не измерил, не знаешь что оптимизировать.

1. Что профайлить

Не "весь код". Только:

  • Hot paths (часто вызываемые)
  • Slow endpoints
  • Узкие места (по жалобам / метрикам)

2. Инструменты

JS / Node.js

  • Chrome DevTools Performance tab (frontend)
  • node --prof (CPU profiling)
  • clinic.js (всё-в-одном)
  • 0x (flame graphs)

Python

  • cProfile (стандарт)
  • py-spy (sampling, без модификаций)
  • memory_profiler

Go

  • pprof (нативно)
  • go tool pprof

Rust

  • perf (Linux)
  • cargo flamegraph

Метрики прод

  • Datadog APM
  • New Relic
  • Sentry Performance
  • Open Telemetry

3. Профилируй с реальной нагрузкой

Микробенчмарки часто врут. Профайль на:

  • Realistic data (не "Foo Bar")
  • Realistic concurrency
  • Realistic data sizes

4. Анализ flame graph

[main 100ms]
├─ [handle_request 95ms]
│  ├─ [query_db 70ms]      ← здесь 70%!
│  ├─ [render 20ms]
│  └─ [other 5ms]
└─ [other 5ms]

Иди от самой широкой "полки" вниз.

5. Категоризируй проблемы

СимптомКатегория
Много CPU на одной функцииAlgorithm complexity
Много времени в malloc / GCMemory allocation
Долгие waits в I/ODatabase, network
Параллельные блокирующие операцииConcurrency
Кеш missCaching

6. Метрики в коде

import { performance } from 'perf_hooks';

const start = performance.now();
await heavyOperation();
const duration = performance.now() - start;

logger.info('Operation took', {
  operation: 'heavyOperation',
  duration_ms: duration,
});

Регулярно — в hot paths.

7. Tracing (distributed)

Если запрос проходит через несколько сервисов:

FrontendAPIDBCacheExternal APItrace span 1trace span 2 (parent: 1)
   ↓
  trace span 3 (parent: 2)

OpenTelemetry даёт это в любом языке.

8. Бенчмарки

Для микро-оптимизаций сравнивай через бенчмарки:

// vitest bench
import { bench } from 'vitest';

bench('Array.map', () => {
  arr.map(x => x * 2);
});

bench('for loop', () => {
  const out = new Array(arr.length);
  for (let i = 0; i < arr.length; i++) out[i] = arr[i] * 2;
});

Запускай несколько раз — стабильно ли?

Подходы к оптимизации

A. Кеширование

Hot calculations → cache (LRU, Redis, etc.).

B. Batching

Множество маленьких операций → одна большая (DB inserts, API calls).

C. Lazy loading

Не загружай пока не нужно.

D. Parallelism

Если операции независимы — Promise.all / threads.

E. Algorithm

O(n²) → O(n log n) / O(n). Часто самое большое улучшение.

F. Data structures

Array.includes (O(n)) → Set.has (O(1)) для частых lookups.

Когда НЕ оптимизировать

  • Cold path (1 раз в год)
  • Микрооптимизации с 0.1% impact
  • Если ухудшает readability сильно
  • Без замеров до/после

Анти-паттерны

  • ❌ "Этот код выглядит медленно" → переписать
  • ❌ Оптимизация в development (без production load)
  • ❌ Использовать "лучший алгоритм" без бенчмарка
  • ❌ Преждевременная параллелизация
  • ❌ Не измерить ПОСЛЕ изменений (может стало хуже)

В конце

  • Текущие hot paths с замерами
  • Топ-5 кандидатов на оптимизацию
  • Expected impact каждого
  • Order to tackle them
Похожие промты