Профилирование Go кода: находим узкие места с pprof и trace

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Профилирование Go кода: находим узкие места

Оптимизация производительности — одна из ключевых задач разработчика, особенно когда речь идет о высоконагруженных системах на Go. Однако прежде чем что-то оптимизировать, нужно точно знать, какие участки кода являются узкими местами. Для этого существует профилирование — сбор и анализ метрик выполнения программы.

В этой статье мы разберем, как профилировать Go-приложения с помощью встроенных инструментов: pprof и trace. Вы узнаете, как находить проблемные места по CPU, памяти, блокировкам и горутинам, а также как интерпретировать полученные данные.

Что такое профилирование и зачем оно нужно?

Профилирование — это процесс сбора данных о работе программы во время её выполнения. Эти данные помогают ответить на вопросы:

  • Какие функции потребляют больше всего процессорного времени?
  • Где происходит выделение большого количества памяти?
  • Какие горутины блокируются и на чем?
  • Сколько времени занимает системный вызов или операция ввода-вывода?

Go предоставляет мощные инструменты для профилирования прямо из коробки. Основные из них — net/http/pprof для веб-приложений и runtime/pprof для CLI-утилит.

Инструмент pprof: CPU и Memory профили

pprof — основной инструмент для профилирования Go. Он умеет собирать профили CPU, памяти, блокировок, горутин и мутекс-конкуренции.

Добавление pprof в веб-приложение

Самый простой способ подключить профилирование — импортировать пакет net/http/pprof:

package main

import (
    "net/http"
    _ "net/http/pprof" // регистрирует обработчики pprof
)

func main() {
    // Ваше приложение
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

После этого по адресу /debug/pprof/ будут доступны различные профили. Например:

  • /debug/pprof/profile — CPU профиль (по умолчанию 30 секунд)
  • /debug/pprof/heap — профиль памяти (heap)
  • /debug/pprof/goroutine — дамп всех горутин
  • /debug/pprof/block — профиль блокировок

Сбор CPU профиля

Для сбора CPU профиля используйте команду:

go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30

После завершения сбора откроется интерактивная консоль pprof. Самые полезные команды:

  • top — показать функции, потребляющие больше всего CPU
  • list <функция> — показать построчную разбивку времени
  • web — открыть граф вызовов в браузере (требуется Graphviz)
  • peek <функция> — показать вызывающие и вызываемые функции

Пример вывода top:

Showing nodes accounting for 3.50s, 70% of 5.00s total
      flat  flat%   sum%        cum   cum%
     1.20s 24.00% 24.00%      1.20s 24.00%  encoding/json.(*Decoder).Decode
     0.80s 16.00% 40.00%      0.80s 16.00%  runtime.mallocgc
     0.60s 12.00% 52.00%      1.80s 36.00%  main.processData
     0.50s 10.00% 62.00%      0.50s 10.00%  runtime.memmove
     0.40s  8.00% 70.00%      0.40s  8.00%  strings.ToLower

Сбор профиля памяти

Для анализа использования памяти:

go tool pprof http://localhost:8080/debug/pprof/heap

Важные команды в pprof для памяти:

  • top — функции, выделяющие больше всего памяти
  • sample_index=alloc_space — переключиться на общее количество выделенной памяти
  • sample_index=inuse_space — переключиться на текущую используемую память
  • tree — показать дерево вызовов с размерами

Профилирование блокировок и горутин

Узкие места часто связаны не только с CPU, но и с блокировками или утечками горутин.

Профиль блокировок (block)

Для включения сбора блокировок необходимо установить долю выборки:

runtime.SetBlockProfileRate(1) // собирать все блокировки

Затем профиль доступен по адресу /debug/pprof/block. Анализ:

go tool pprof http://localhost:8080/debug/pprof/block

Профиль мутекс-конкуренции (mutex)

Аналогично для мутексов:

runtime.SetMutexProfileFraction(1)

// Сбор
go tool pprof http://localhost:8080/debug/pprof/mutex

Дамп горутин

Иногда нужно просто посмотреть, сколько горутин запущено и что они делают:

wget http://localhost:8080/debug/pprof/goroutine?debug=2 -O goroutines.txt

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

Инструмент trace: визуализация выполнения

Пакет runtime/trace предоставляет более детальную картину выполнения программы: временные шкалы горутин, события GC, системные вызовы и т.д.

Запись трассировки

package main

import (
    "os"
    "runtime/trace"
)

func main() {
    f, _ := os.Create("trace.out")
    defer f.Close()
    
    trace.Start(f)
    defer trace.Stop()
    
    // Ваш код
    processData()
}

Для просмотра трассировки используйте команду:

go tool trace trace.out

                
                

Блоги

Рекомендаций по книгам