Оптимизация без измерения - это изменение кода с надеждой. Правильный цикл: метрика, гипотеза, базовый замер, профиль, изменение, повторная проверка.
Оптимизация - повторяемый эксперимент. Без базового и контрольного замера улучшение остается догадкой.
Сначала вопрос
Формулируем измеримую проблему:
- p99 обработчика больше 300 мс при 500 RPS;
- 150 аллокаций на запрос и частые GC;
- 40 тысяч горутин после суток работы;
- сервис не держит требуемые 1000 RPS.
У каждой проблемы свой профиль. Latency не всегда означает CPU, рост памяти не всегда утечка кучи, а медленная база не всегда плохой Go-код.
Базовый замер
Фиксируем версию, окружение, данные, нагрузку и прогрев. Сравнивать разные машины и случайные данные бессмысленно.
Минимальный набор:
- p50/p95/p99 и долю ошибок;
- RPS фактический, а не только заданный;
- CPU, память, число горутин;
- задержки зависимостей;
- время теста и состояние кэшей.
Базовый результат сохраняется до изменения. Иначе «стало быстрее» доказать нечем.
Гипотеза
Хорошая гипотеза объясняет причину и предсказывает эффект: «сборка строки через fmt.Sprintf создаёт большую часть аллокаций, замена на strings.Builder снизит их вдвое и уменьшит долю GC в CPU».
После гипотезы выбираем профиль: heap для аллокаций, CPU для процессора, goroutine для ожидания, trace для планировщика, метрики зависимости для сети и базы.
Одно изменение за раз
Меняем одну причину, повторяем тот же сценарий. Если одновременно поменять SQL, сериализацию и пул, невозможно понять вклад каждой.
Результат сравниваем по абсолютным значениям и статистической устойчивости. Один быстрый прогон ничего не доказывает: нужны повторы и сравнение распределения.
Коротко
- Сначала измеримая проблема, затем код.
- Базовый замер включает окружение и нагрузку.
- Гипотеза предсказывает и причину, и эффект.
- Одно изменение за раз, повторные проверки обязательны.