Материал проходит вычитку — возможны неточности.
Урок 30 из 118

Сквозной пример

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

Главная мысль

профиль находит реальное узкое место, а не предполагаемое: в примере — регулярка, компилируемая в цикле, съедает и CPU, и аллокации, и всё лечится выносом компиляции из цикла.

Программа с узким местом

Программа валидирует список почтовых адресов регуляркой и приводит их к нижнему регистру:

func validate(addresses []string) []string {
    var out []string
    for _, a := range addresses {
        re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
        if re.MatchString(a) {
            out = append(out, strings.ToLower(a))
        }
    }
    return out
}

validate компилирует регулярку внутри цикла — по разу на каждый адрес, 250 тысяч раз за прогон. Снимаем профили через runtime/pprof и смотрим.

Что показывают профили

В top по CPU наверху окажется сам runtime: сборщик мусора крутится почти без остановки. Среди прикладного кода самый жирный кусок — компиляция регулярки: regexp.MustCompile и функции из regexp/syntax. Heap-профиль в alloc_space подтверждает: почти все суммарные аллокации — те же компиляции: дерево разбора и программа строятся заново для каждого адреса. В нашем замере это около гигабайта на 250 тысяч адресов — число конкретного прогона, зависит от данных и версии Go, а не универсальный исход.

Лечим и проверяем

Лечится выносом в пакетную переменную — шаблон компилируется один раз при старте:

var emailRe = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)

После правок повторный профиль рисует другую картину: в нашем замере прогон укладывается в полсекунды вместо трёх (цифры конкретного прогона — зависят от данных, машины и версии Go), компиляция из профиля исчезла, а вместе с ней успокоился и GC. Общий рецепт такой: выросла задержка или выросло CPU — CPU-профиль, top и flame graph, ищем самую жирную ветку; растёт память или дёргается GC — heap-профиль в inuse_space для живых объектов и в alloc_space для источника аллокаций. Профиль всегда показывает, что реально происходит, а не что ты думаешь про свой код.

В следующем уроке

Вы узнаете:

  • какой полный список профилей pprof и когда каждый нужен
  • как профилируют в проде канареечными сэмплами
Задание · вопросы

Проверьте себя

Вопрос 1 из 3

regexp.MustCompile внутри цикла по 250 000 адресам. Что это даёт?

Вопрос 2 из 3

Как лечится компиляция регулярки в цикле?

Вопрос 3 из 3

Профилируют до или после оптимизации?

Итоги

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

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