Глубокая расшифровка pprof на языке Go

Go

Думаю, многие слышали историю «Тора 3» об оптимизации производительности. В исходном коде движка 3D-игры Джон Кармак будет1/sqrt(x)Эффективность выполнения этой функции максимально оптимизирована.

Обычно мы используем метод деления пополам или метод итерации Ньютона для вычисления квадратного корня из числа с плавающей запятой. Но в этой функции автор использует «магическое число», итерации нет вообще, а квадратный корень вычисляется напрямую в два шага. Захватывающий!

Поскольку это функция самого низкого уровня, а игра включает в себя большое количество таких операций, игра может работать гладко в эпоху DOS, где вычислительные ресурсы чрезвычайно ограничены. В этом вся прелесть оптимизации производительности!

На работе, когда объем бизнеса относительно невелик, используется меньше машин, и преимущества оптимизации производительности не могут быть реализованы. Когда бизнес использует тысячи машин и оптимизирует производительность на 20 %, он может сэкономить сотни машин и миллионы долларов в год. Сэкономленные деньги дадут сотрудникам премии в конце года, так много счастья!

Вообще говоря, анализ производительности можно рассматривать на трех уровнях: уровень приложения, уровень системы и уровень кода.

Прикладной уровень предназначен в основном для того, чтобы разобраться с использованием бизнес-стороны, позволить им использовать его более разумно и сократить количество бессмысленных вызовов, исходя из предпосылки удовлетворения потребностей пользователя; системный уровень обращает внимание на архитектуру службы, например как добавление уровня кеша; уровень кода заботится об эффективности выполнения функции, например, об использовании более эффективного алгоритма квадратного корня и т. д.

Чтобы сделать что-либо, вы должны обратить внимание на метод. Во многих случаях быстрое выполнение самой важной части задач может принести большую часть преимуществ. Некоторые другие края и углы можно сшивать медленно. Как только вы захотите выполнить на 100%, вы часто будете попадать в ситуацию, когда приложили много усилий и мало достигли.

То же самое верно и для оптимизации производительности: выявление узких мест производительности позволит нам заплатить наименьшие усилия и получить наибольшую награду.

В языке Go pprof — это такой инструмент, который помогает нам быстро находить узкие места в производительности, а затем выполнять целевые оптимизации.

что такое ппроф

Перед запуском кода мы можем получить производительность системы посредством нагрузочного тестирования, например, количество запросов, которые можно обработать в секунду, среднее время отклика, процент ошибок и другие показатели. Таким образом, мы получаем итоговую оценку эффективности наших услуг.

Но измерение стресса — это смоделированный трафик в автономном режиме, что, если он выйдет в онлайн? Будут такие сценарии, как высокий параллелизм, большой трафик, ненадежность восходящего и нисходящего потоков, внезапный пиковый трафик и т. д., которые непредсказуемы.

В сети внезапно появляется большое количество алармов, таймаут интерфейса, количество ошибок увеличивается.Помимо просмотра логов и мониторинга, вы можете использовать инструменты анализа производительности, чтобы проанализировать производительность программы и найти узкое место. Конечно, вообще эта ситуация не даст вам возможности проанализировать, даунгрейд, текущий лимит, откат - это первое, что нужно сделать, сначала нужно остановить лосс. После восстановления нормальной работы используйте онлайн-воспроизведение трафика или стресс-тестирование, чтобы создать проблемы с производительностью, а затем используйте инструменты для анализа узких мест системы.

Вообще говоря, анализ производительности в основном фокусируется на таких показателях, как ЦП, память, дисковый ввод-вывод и сеть.

ProfilingЭто относится к набору данных, которые могут отражать состояние выполнения программы во время выполнения программы. В разработке программного обеспечения анализ производительности (также называемый профилированием) — это метод анализа, который изучает поведение программы путем сбора информации о времени выполнения программы, а также метод динамического анализа программы.

Библиотека pprof, входящая в состав языка Go, может анализировать работу программы и предоставлять функции визуализации. Он содержит две связанные библиотеки:

  • время выполнения/pprof Для программ, которые запускаются только один раз, например автономных программ предварительной обработки, которые запускаются только один раз в день, вызовите функции, предоставляемые пакетом pprof, чтобы вручную включить сбор данных о производительности.

  • сеть/http/pprof Для онлайн-сервисов, для HTTP-сервера, получите доступ к HTTP-интерфейсу, предоставляемому pprof, для получения данных о производительности. Конечно, на самом деле нижним уровнем также является функция, предоставляемая исполняемой средой/pprof, которая инкапсулируется в интерфейс для обеспечения доступа к внешней сети.

Роль pprof

pprofЭто инструмент для анализа производительности программ на языке Go, который может предоставлять различные данные о производительности:

pprof 采集的信息类型

allocsиheapИнформация о выборке такая же, но первая — это выделение памяти для всех объектов, а куча — это выделение памяти для активных объектов.

The difference between the two is the way the pprof tool reads there at start time. Allocs profile will start pprof in a mode which displays the total number of bytes allocated since the program began (including garbage-collected bytes).

Изображение выше взято из практической статьи pprof в справочнике [wolfogre], в которой приводится пример программы для устранения неполадок, анализа и решения проблем с производительностью с помощью pprof, что очень интересно.

  1. Когда анализ производительности ЦП включен, среда выполнения Go будет приостанавливаться каждые 10 мс, чтобы записать стек вызовов и связанные данные текущей запущенной горутины. После сохранения данных анализа производительности на жесткий диск мы можем проанализировать горячие точки в коде.
  1. Анализ производительности памяти заключается в записи стека вызовов при выделении кучи. По умолчанию выборка выполняется каждые 1000 выделений, это значение можно изменить. Выделения стека не записываются анализом памяти, поскольку они освобождаются в любое время. Потому что профилирование памяти — это выборка, а также потому, что оно записывает выделение памяти, а не использование памяти. Поэтому трудно использовать инструменты анализа производительности памяти, чтобы точно оценить конкретное использование памяти программой.
  1. Анализ блокировки — это очень уникальный анализ, он чем-то похож на анализ производительности ЦП, но фиксирует время, затрачиваемое горутинами на ожидание ресурсов. Анализ блокировки очень полезен для анализа узких мест параллелизма программы.Анализ производительности блокировки может показать, когда заблокировано большое количество горутин. Блокирующее профилирование — это специальный инструмент профилирования, который не следует использовать для профилирования, пока не будут устранены узкие места ЦП и памяти.

Как использовать ппроф

мы можем пройти报告生成,Web 可视化界面,交互式终端три способа использованияpprof.

—— Жареная рыба «Анализ производительности Golang Killer PProf»

runtime/pprof

Возьмите в качестве примера профилирование ЦП, добавьте две строки кода, вызовитеpprof.StartCPUProfileЗапустить профилирование процессора, позвонитьpprof.StopCPUProfile()Сбросить данные в файл:

import "runtime/pprof"

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func main() {
    // …………
        
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
    
    // …………
}

net/http/pprof

Запустите порт (отличный от порта, который обычно предоставляет бизнес-услуги) для прослушивания запросов pprof:

import _ "net/http/pprof"

func initPprofMonitor() error {
	pPort := global.Conf.MustInt("http_server", "pprofport", 8080)

	var err error
	addr := ":" + strconv.Itoa(pPort)

	go func() {
		err = http.ListenAndServe(addr, nil)
		if err != nil {
			logger.Error("funcRetErr=http.ListenAndServe||err=%s", err.Error())
		}
	}()

	return err
}

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

// src/net/http/pprof/pprof.go:71

func init() {
	http.Handle("/debug/pprof/", http.HandlerFunc(Index))
	http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
	http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
	http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
	http.Handle("/debug/pprof/trace", http.HandlerFunc(Trace))
}

первый путь/debug/pprof/На самом деле есть 5 подпутей ниже:

goroutine threadcreate heap block mutex

Запустив сервис, получите к нему доступ прямо в браузере:

http://47.93.238.9:8080/debug/pprof/

Вы можете получить сводную страницу:

页面 /debug/pprof

Вы можете напрямую щелкнуть ссылку выше, чтобы перейти на подстраницу для просмотра соответствующей сводной информации.

Есть две ссылки для получения информации о горутинах,goroutineиfull goroutine stack dump, первое представляет собой сводное сообщение, вы можете просмотреть общую ситуацию с горутинами, а второе — статус каждой горутины. Для интерпретации конкретного содержания страницы, пожалуйста, обратитесь к статье [Да Бин].

нажмитеprofileиtraceВыборка данных будет выполняться в фоновом режиме в течение определенного периода времени. После завершения выборки файл профиля будет возвращен в браузер, а затем передан локально.go tool pprofинструменты для анализа.

Когда скачаем и получим файл профиля, выполняем команду:

go tool pprof ~/Downloads/profile

pprof profile

Вы можете войти в интерактивный режим использования командной строки. воплощать в жизньgo tool pprof -helpСправочную информацию можно просмотреть.

Вы можете войти в интерактивный режим командной строки, не щелкая ссылку в браузере, напрямую используя следующую команду:

go tool pprof http://47.93.238.9:8080/debug/pprof/profile

Конечно, также необходимо собирать данные в фоновом режиме в течение определенного периода времени, затем загружать файл данных на локальный компьютер и, наконец, анализировать его. За приведенным выше URL также может следовать параметр времени:?seconds=60, чтобы настроить продолжительность профилирования ЦП.

Аналогичные команды:

# 下载 cpu profile,默认从当前开始收集 30s 的 cpu 使用情况,需要等待 30s
go tool pprof http://47.93.238.9:8080/debug/pprof/profile
# wait 120s
go tool pprof http://47.93.238.9:8080/debug/pprof/profile?seconds=120     

# 下载 heap profile
go tool pprof http://47.93.238.9:8080/debug/pprof/heap

# 下载 goroutine profile
go tool pprof http://47.93.238.9:8080/debug/pprof/goroutine

# 下载 block profile
go tool pprof http://47.93.238.9:8080/debug/pprof/block

# 下载 mutex profile
go tool pprof http://47.93.238.9:8080/debug/pprof/mutex

После входа в интерактивный режим наиболее часто используемыеtop,list,webи так далее.

воплощать в жизньtop:

pprof top

Получите четыре столбца данных:

имя столбца значение
flat Время выполнения этой функции
flat% Flat в процентах от общего времени процессора. Общее время программы составило 16,22 с, а 16,19 с Eat составили 99,82 %.
sum% Сумма плоских пропорций каждой предыдущей строки
cum накопительная сумма. Относится к общему времени, затраченному функцией, плюс функции, вызываемые функцией.
cum% cum в процентах от общего времени процессора

Другие типы, такие как heap's flat, sum и cum, аналогичны по смыслу вышеприведенным, но вычисления отличаются: один — это процессорное время, а другой — размер памяти.

воплощать в жизньlist,использовать正则Сопоставьте, найдите соответствующий код:

list Eat

Непосредственно найдите соответствующий код, отнимающий много времени:

pprof list

воплощать в жизньweb(Вам необходимо установить graphviz, pprof может использовать grapgviz для генерации графа вызовов программы), он сгенерирует файл формата svg и откроет его прямо в браузере (возможно, вам потребуется установить метод открытия файла .svg по умолчанию). формат):

pprof web

Линия на рисунке представляет вызов метода, а метка на линии представляет выборочное значение указанного вызова метода (например, время, размер выделения памяти и т. д.), а размер поля связан с размером выборочное значение выполняемого метода.

Каждое поле состоит из двух меток: в профиле процессора одна — это доля времени, в течение которого выполняется метод, а другая — доля времени, в течение которой он появляется в выборочном стеке (первое — фиксированное время, второе — кумулятивное время). пропорциональное соотношение); чем больше поле, тем больше времени или больше памяти выделяется.

Кроме того,tracesКоманда также может отображать стек вызовов функции:

pprof traces

Помимо двух упомянутых выше способов (генерация отчета, взаимодействие с командной строкой), вы также можете взаимодействовать в браузере. Сначала создайте файл профиля, а затем выполните команду:

go tool pprof --http=:8080 ~/Downloads/profile

Войдите в визуальный рабочий интерфейс:

pprof 可视化界面

Нажмите на строку меню, чтобы переключиться между: Top/Graph/Peek/Source и даже увидеть Flame Graph:

pprof 火焰图

По сравнению с общим графиком пламени он просто перевернут, и отношения вызова отображаются сверху вниз. Чем длиннее форма, тем дольше время выполнения. Примечание. Используемая здесь версия go — 1.13, а более старые версии инструмента pprof не поддерживаются.-httpпараметр. Конечно, вы можете скачать и другие библиотеки для просмотра флейм-графа, например:

go get -u github.com/google/pprof

或者

go get github.com/uber/go-torch

ппроф продвинутый

Я дал несколько практических статей по использованию инструмента pprof для анализа производительности в разделе «Ссылки», вы можете попрактиковаться в нем, а затем использовать его в своей обычной работе.

Расс Кокс в действии

Основное содержание этой части взято из справочного материала [Росс Кокс] и посвящено идеям Дэниела по оптимизации.

Причина этого в том, что кто-то опубликовал статью, в которой реализован алгоритм на разных языках, и программа, написанная на go, была очень медленной, а C++ — самой быстрой. А потом Расс Кокс разозлился, как он мог это выдержать? Теперь включите pprof killer для оптимизации. В конце концов, программа не только быстрее, но и использует меньше памяти!

Во-первых, добавьте код для профилирования процессора:

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func main() {
    flag.Parse()
    if *cpuprofile != "" {
        f, err := os.Create(*cpuprofile)
        if err != nil {
            log.Fatal(err)
        }
        
        pprof.StartCPUProfile(f)
        defer pprof.StopCPUProfile()
    }
    ...
}

Наблюдение за прошедшим временем с помощью pproftop5, и обнаружил, что функция, считывающая карту, занимает больше всего времени:mapaccess1_fast64, в то время как он появляется в рекурсивной функции.

web

С первого взгляда вы можете увидеть самую большую рамкуmapacess1_fast64функция. воплощать в жизньweb mapaccess1Команда, более сосредоточенная:

web mapaccess1

перечислитьmapaccess1_fast64Больше всего функций main.FindLoops и main.DFS, пора найти конкретный код и выполнить команду:list DFS, найдите соответствующий код.

Метод оптимизации состоит в том, чтобы изменить карту на срез, Причина этого, конечно, в том, что тип ключа - int и он не слишком разреженный.

Вывод будет заключаться в том, что для небольших наборов данных вам не следует использовать карты, где было бы достаточно срезов, поскольку карты имеют большие накладные расходы.

После модификации, снова путем профилирования процессора, обнаружено, что рекурсивные функции, отнимающие много времени, больше не входят в топ-5. Однако добавлена ​​долгая по времени функция: runtime.mallocgc, на долю которой приходится 54,2%, что связано с выделением памяти и сборкой мусора.

Затем добавьте код для сбора данных памяти:

var memprofile = flag.String("memprofile", "", "write memory profile to this file")

func main() {
    // …………
    
    FindHavlakLoops(cfgraph, lsgraph)
    if *memprofile != "" {
        f, err := os.Create(*memprofile)
        if err != nil {
            log.Fatal(err)
        }
        pprof.WriteHeapProfile(f)
        f.Close()
        return
    }
    
    // …………
}

продолжить черезtop5,listКоманда находит участок кода, который выделяет больше всего памяти, и обнаруживает, что на этот раз при вставке элементов в карту используется больше памяти. Метод улучшения также заключается в использовании среза вместо карты, но еще одна особенность карты заключается в том, что элементы могут быть вставлены повторно, поэтому написана новая функция для вставки элементов в срез:

func appendUnique(a []int, x int) []int {
    for _, y := range a {
        if x == y {
            return a
        }
    }
    return append(a, x)
}

Что ж, теперь программа работает в 2,1 раза быстрее, чем была изначально. Проверьте данные профиля процессора еще раз и найдите, чтоruntime.mallocgcОн немного снизился, но по-прежнему составляет 50,9%.

Another way to look at why the system is garbage collecting is to look at the allocations that are causing the collections, the ones that spend most of the time in mallocgc.

Следовательно, необходимо видеть, что перерабатывается при сборке мусора, что является «виновником», вызывающим частую сборку мусора.

использоватьweb mallocgcкоманда для отображения функций, связанных с mallocgc, в виде векторной диаграммы, но слишком много узлов с малым размером выборки, которые влияют на наблюдение, добавьте команду фильтра:

go tool pprof --nodefraction=0.1 profile

web mallocgc

будет меньше, чем10%Точки выборки , отфильтровываются, и новую векторную диаграмму можно увидеть интуитивно,FindLoopsИнициировано наибольшее количество операций по сбору мусора. продолжайте использовать командуlist FindLoopsНайдите местоположение кода напрямую.

Получается, что каждый раз, когда вы выполняетеFindLoopsфункции, обаmakeНекоторые временные переменные, которые будут облагать налогом сборщик мусора. Улучшение заключается в добавлении кэша глобальных переменных, который можно использовать повторно. Недостатком является то, что теперь он не является потокобезопасным.

На этом оптимизация с помощью инструмента pprof завершается. Конечный результат очень хорош, в основном достигается та же скорость и тот же размер выделения памяти, что и в C++.

Вдохновение, которое мы можем получить, состоит в том, чтобы сначала использовать профиль процессора, чтобы найти наиболее трудоемкие функции и оптимизировать их. Если вы обнаружите, что gc выполняет много, найдите код, который выделяет больше всего памяти, и функцию, которая вызывает выделение памяти, и оптимизируйте ее.

Оригинальный текст отличный, и хотя он был написан давно (изначально написан в 2011 году), его все же стоит посмотреть. Кроме того, реальная боевая статья из справочного материала [wolfogre] также очень интересна, и используемые движения аналогичны этой статье, но вы можете запустить пример программы, представленный в статье, чтобы шаг за шагом решить проблему с производительностью, очень интересный!

Найдите утечки памяти

Выделение памяти может происходить либо в куче, либо в стеке. Память, выделенная в куче, требует сборки мусора или ручной сборки (для языков без сборки мусора, таких как C++), память в стеке обычно освобождается автоматически после выхода из функции.

Язык Go использует escape-анализ для размещения в стеке как можно большего количества объектов, чтобы программа могла работать быстрее.

Здесь, чтобы объяснить, есть две стратегии анализа памяти: одна - это текущая (на этот раз получение) память или выделение объекта, называемоеinuse; другой — это все выделения памяти с момента запуска программы, независимо от того, была ли это сборка мусора или нет, вызываемаяalloc.

As mentioned above, there are two main memory analysis strategies with pprof. One is around looking at the current allocations (bytes or object count), called inuse. The other is looking at all the allocated bytes or object count throughout the run-time of the program, called alloc. This means regardless if it was gc-ed, a summation of everything sampled.

плюс-sample_indexПосле параметра можно переключить тип анализа памяти:

go tool pprof -sample_index=alloc_space  http://47.93.238.9:8080/debug/pprof/heap

Есть 4 типа:

тип значение
inuse_space amount of memory allocated and not released yet
inuse_objects amount of objects allocated and not released yet
alloc_space total amount of memory allocated (regardless of released)
alloc_objects total amount of objects allocated (regardless of released)

В справочном материале [Dabin Actual Memory Leakage] описывается, как найти дополнительные горутины в два момента до и после с помощью метода, аналогичного diff, а затем найти причину утечки горутины и не использовать напрямую профиль кучи или горутины. файлы. Также рекомендуется к прочтению!

Суммировать

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

После получения данных о производительности вы можете использоватьtop,web,listПодождите, пока команда быстро найдет соответствующий код и оптимизирует его.

«Преждевременная оптимизация — корень всех зол». В реальной работе мало кто обращает внимание на производительность, но когда программа, которую вы пишете, имеет узкие места в производительности, и qps не может подняться во время стресс-теста qa, чтобы продемонстрировать техническую силу, вам все равно нужно наблюдать узкие места в производительности через pprof, и выполнять соответствующую оптимизацию производительности.

использованная литература

[Процесс оптимизации Расса Кокса с прикрепленным кодом]blog.golang.org/profiling-a…

【гугл ппроф】GitHub.com/Google/BubbleRO…

[Отладка приложений golang с помощью графиков pprof и flame]secondchildstudent.com/2017/09/11/…

【Сбор ресурсов】Добавь V и ты готов.ru/blog/2017/0…

【Профилирование вашего приложения Golang за 3 шага】coder.today/специальности/2018-1…

[Кейс, стресс-тест, удаленное профилирование Golang и пламенные графы]МА на ski.com/article/ Иди сюда…

【Жареная рыба ппроф】сегмент fault.com/ah/119000001…

【Птичье гнездо ппроф】коло not.com/2017/03/02/…

[7 методов профилирования для Go]blog.horn99.org/post/go wave…

[pprof более полный]nuggets.capable/post/684490…

[Объясните процесс анализа и оптимизации на примерах]арт эм. Гостей все меньше и меньше. V.com/blog/2017/0…

[Переход от Дмитрия Вьюкова]GitHub.com/gowaves/go/me…

[Очень замечательная практическая статья Вольфогре]blog.wolf ogre.com/posts/go-brand…

【Дэйв Чейни】Dave.Cheney.net/high-per для…

【Реальный случай】блог woo woo woo.cn on.com/loss может быть 303/afraid…

[Настоящая утечка памяти Дабина]сегмент fault.com/ah/119000001…

【Найти утечку памяти】Woohoo.бесплатный код camp.org/news/how-i-…

【Оптимизация производительности Thor 3】Imperial Capital coder.com/so попробуйте-о…