Анализируйте производительность производства с помощью Cloud Profiler

Анализируйте производительность производства с помощью Cloud Profiler

О практической работе

subjectПоследнее обновление: июл. 14, 2021
account_circleАвторы: aalexand

1. Обзор

Хотя разработчики клиентских приложений и веб-интерфейсов обычно используют такие инструменты, как Android Studio CPU Profiler или инструменты профилирования, включенные в Chrome, для повышения производительности своего кода, эквивалентные методы не так доступны и не так хорошо приняты теми, кто работает над серверными службами. Cloud Profiler предоставляет те же возможности разработчикам сервисов, независимо от того, работает ли их код на Google Cloud Platform или где-либо еще.

95c034c70c9cac22.png

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

В этой лабораторной работе вы узнаете, как настроить Cloud Profiler для программы Go, и познакомитесь с тем, какую информацию о производительности приложений может предоставить этот инструмент.

Что вы узнаете

  • Как настроить программу Go для профилирования с помощью Cloud Profiler.
  • Как собирать, просматривать и анализировать данные о производительности с помощью Cloud Profiler.

Что вам понадобится

  • Проект облачной платформы Google
  • Браузер, например Chrome или Firefox.
  • Знакомство со стандартными текстовыми редакторами Linux, такими как Vim, EMAC или Nano.

Как вы будете использовать это руководство?

Как бы вы оценили свой опыт работы с Google Cloud Platform?

2. Настройка и требования

Самостоятельная настройка среды

  1. Войдите в Cloud Console и создайте новый проект или повторно используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Запомните идентификатор проекта — уникальное имя для всех проектов Google Cloud (имя, указанное выше, уже занято и не подойдет вам, извините!). Позже в этой лаборатории он будет называться PROJECT_ID .

  1. Далее вам необходимо включить биллинг в Cloud Console, чтобы использовать ресурсы Google Cloud.

Прохождение этой лаборатории кода не должно стоить много, если вообще стоит. Обязательно следуйте всем инструкциям в разделе «Очистка», в которых рассказывается, как отключить ресурсы, чтобы вам не приходилось нести расходы, выходящие за рамки этого руководства. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .

Google Cloud Shell

Хотя Google Cloud можно управлять удаленно с вашего ноутбука, для упрощения настройки в этой лаборатории мы будем использовать Google Cloud Shell , среду командной строки, работающую в облаке.

Активировать Cloud Shell

  1. В Cloud Console нажмите «Активировать Cloud Shell». 4292cbf4971c9786.png .

bce75f34b2c53987.png

Если вы никогда раньше не запускали Cloud Shell, вам будет представлен промежуточный экран (ниже сгиба) с описанием того, что это такое. В этом случае нажмите «Продолжить» (и вы больше никогда этого не увидите). Вот как выглядит этот одноразовый экран:

70f315d7b402b476.png

Подготовка и подключение к Cloud Shell займет всего несколько минут.

fbe3a0674c982259.png

Эта виртуальная машина оснащена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Большую часть, если не всю, работу в этой лаборатории кода можно выполнить с помощью просто браузера или Chromebook.

После подключения к Cloud Shell вы увидите, что вы уже прошли аутентификацию и что для проекта уже установлен идентификатор вашего проекта.

  1. Выполните следующую команду в Cloud Shell, чтобы подтвердить, что вы прошли аутентификацию:
gcloud auth list

Вывод команды

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте:
gcloud config list project

Вывод команды

[core]
project = <PROJECT_ID>

Если это не так, вы можете установить его с помощью этой команды:

gcloud config set project <PROJECT_ID>

Вывод команды

Updated property [core/project].

3. Перейдите в Cloud Profiler.

В Cloud Console перейдите к пользовательскому интерфейсу профилировщика, нажав «Профилировщик» на левой панели навигации:

37ad0df7ddb2ad17.png

Альтернативно вы можете использовать панель поиска Cloud Console для перехода к пользовательскому интерфейсу профилировщика: просто введите «Cloud Profiler» и выберите найденный элемент. В любом случае вы должны увидеть пользовательский интерфейс профилировщика с сообщением «Нет данных для отображения», как показано ниже. Проект новый, поэтому данные для его профилирования еще не собраны.

d275a5f61ed31fb2.png

Пришло время что-то профилировать!

4. Профилируйте эталон

Мы будем использовать простое синтетическое приложение Go , доступное на Github . В терминале Cloud Shell, который у вас все еще открыт (и пока сообщение «Нет данных для отображения» все еще отображается в пользовательском интерфейсе профилировщика), выполните следующую команду:

$ go get -u github.com/GoogleCloudPlatform/golang-samples/profiler/...

Затем переключитесь в каталог приложения:

$ cd ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/profiler/hotapp

Каталог содержит файл «main.go», который представляет собой синтетическое приложение с включенным агентом профилирования:

main.go

...
import (
       
...
       
"cloud.google.com/go/profiler"
)
...
func main() {
       
err := profiler.Start(profiler.Config{
               
Service:        "hotapp-service",
               
DebugLogging:   true,
               
MutexProfiling: true,
       
})
       
if err != nil {
               
log.Fatalf("failed to start the profiler: %v", err)
       
}
       
...
}

Агент профилирования по умолчанию собирает профили ЦП, кучи и потоков. Приведенный здесь код позволяет собирать профили мьютексов (также известных как «конкурентные»).

Теперь запустите программу:

$ go run main.go

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

$ go run main.go
2018/03/28 15:10:24 profiler has started
2018/03/28 15:10:57 successfully created profile THREADS
2018/03/28 15:10:57 start uploading profile
2018/03/28 15:11:19 successfully created profile CONTENTION
2018/03/28 15:11:30 start uploading profile
2018/03/28 15:11:40 successfully created profile CPU
2018/03/28 15:11:51 start uploading profile
2018/03/28 15:11:53 successfully created profile CONTENTION
2018/03/28 15:12:03 start uploading profile
2018/03/28 15:12:04 successfully created profile HEAP
2018/03/28 15:12:04 start uploading profile
2018/03/28 15:12:04 successfully created profile THREADS
2018/03/28 15:12:04 start uploading profile
2018/03/28 15:12:25 successfully created profile HEAP
2018/03/28 15:12:25 start uploading profile
2018/03/28 15:12:37 successfully created profile CPU
...

Пользовательский интерфейс обновится вскоре после сбора первого профиля. После этого он не будет обновляться автоматически, поэтому, чтобы увидеть новые данные, вам придется обновить пользовательский интерфейс профилировщика вручную. Для этого дважды нажмите кнопку «Сейчас» в выборе временного интервала:

650051097b651b91.png

После обновления пользовательского интерфейса вы увидите что-то вроде этого:

47a763d4dc78b6e8.png

Селектор типа профиля показывает пять доступных типов профиля:

b5d7b4b5051687c9.png

Давайте теперь рассмотрим каждый из типов профилей и некоторые важные возможности пользовательского интерфейса, а затем проведем несколько экспериментов. На этом этапе вам больше не нужен терминал Cloud Shell, поэтому вы можете выйти из него, нажав CTRL-C и набрав «exit».

5. Анализ данных профилировщика

Теперь, когда мы собрали некоторые данные, давайте посмотрим на них более внимательно. Мы используем синтетическое приложение (исходный код доступен на Github ), которое имитирует поведение, типичное для различных проблем с производительностью в производстве.

Код, интенсивно использующий процессор

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

fae661c9fe6c58df.png

Эта функция специально написана так, чтобы потреблять много циклов процессора за счет выполнения жесткого цикла:

main.go

func load() {
        for i := 0; i < (1 << 20); i++ {
        }
}

Функция вызывается косвенно из busyloop () через четыре пути вызова: busyloop → { foo1 , foo2 } → { bar , baz } → load . Ширина функционального поля представляет собой относительную стоимость конкретного пути вызова. В этом случае все четыре пути имеют примерно одинаковую стоимость. В реальной программе вы хотите сосредоточиться на оптимизации путей вызовов, которые наиболее важны с точки зрения производительности. Пламенный график, который визуально выделяет более дорогие пути более крупными прямоугольниками, позволяет легко идентифицировать эти пути.

Вы можете использовать фильтр данных профиля для дальнейшего уточнения отображения. Например, попробуйте добавить фильтр «Показать стеки», указав в качестве строки фильтра «baz». Вы должны увидеть что-то вроде снимка экрана ниже, где отображаются только два из четырех путей вызова метода load() . Эти два пути — единственные, которые проходят через функцию со строкой «baz» в имени. Такая фильтрация полезна, когда вы хотите сосредоточиться на части более крупной программы (например, потому что вы владеете только ее частью).

eb1d97491782b03f.png

Код, интенсивно использующий память

Теперь переключитесь на тип профиля «Куча». Обязательно удалите все фильтры, созданные вами в предыдущих экспериментах. Теперь вы должны увидеть график пламени, где allocImpl , вызываемый alloc , отображается как основной потребитель памяти в приложении:

f6311c8c841d04c4.png

Сводная таблица над графиком пламени показывает, что общий объем используемой памяти в приложении составляет в среднем ~57,4 МБ, большая часть которой выделяется функцией allocImpl . Это неудивительно, учитывая реализацию этой функции:

main.go

func allocImpl() {
        // Allocate 64 MiB in 64 KiB chunks
        for i := 0; i < 64*16; i++ {
                mem = append(mem, make([]byte, 64*1024))
        }
}

Функция выполняется один раз, выделяя 64 МБ на более мелкие фрагменты, а затем сохраняя указатели на эти фрагменты в глобальной переменной, чтобы защитить их от сбора мусора. Обратите внимание, что объем памяти, используемый профилировщиком, немного отличается от 64 МБ: профилировщик кучи Go — это статистический инструмент, поэтому измерения выполняются с низкими издержками, но не с точностью до байта. Не удивляйтесь, увидев такую ​​разницу в ~10%.

Код с интенсивным вводом-выводом

Если вы выберете «Потоки» в селекторе типа профиля, отображение переключится на график пламени, где большую часть ширины занимают функции wait и waitImpl :

ebd57fdff01dede9.png

В сводном графике пламени выше вы можете видеть, что существует 100 горутин, которые увеличивают свой стек вызовов из функции wait . Это совершенно верно, учитывая, что код, инициирующий эти ожидания, выглядит так:

main.go

func main() {
        ...
        // Simulate some waiting goroutines.
        for i := 0; i < 100; i++ {
                go wait()
        }

Этот тип профиля полезен для понимания того, проводит ли программа какое-либо неожиданное время ожидания (например, ввода-вывода). Такие стеки вызовов обычно не выбираются профилировщиком ЦП, поскольку они не потребляют значительной части процессорного времени. Вы часто захотите использовать фильтры «Скрыть стеки» с профилями потоков — например, чтобы скрыть все стеки, заканчивающиеся вызовом gopark, поскольку они часто являются простаивающими горутинами и менее интересны, чем те, которые ожидают ввода-вывода.

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

Конфликтный код

Тип профиля конкуренции определяет наиболее «желательные» блокировки в программе. Этот тип профиля доступен для программ Go, но его необходимо явно включить, указав « MutexProfiling: true » в коде конфигурации агента. Коллекция работает путем записи (в метрике «Конфликты») количества раз, когда конкретная блокировка при разблокировке горутиной A заставляла другую горутину B ожидать разблокировки блокировки. Он также записывает (в метрике «Задержка») время, в течение которого заблокированная горутина ждала блокировки. В этом примере имеется один конкурирующий стек, а общее время ожидания блокировки составило 10,5 секунд:

83f00dca4a0f768e.png

Код, генерирующий этот профиль, состоит из 4 горутин, борющихся за мьютекс:

main.go

func contention(d time.Duration) {
        contentionImpl(d)
}

func contentionImpl(d time.Duration) {
        for {
                mu.Lock()
                time.Sleep(d)
                mu.Unlock()
        }
}
...
func main() {
        ...
        for i := 0; i < 4; i++ {
                go contention(time.Duration(i) * 50 * time.Millisecond)
        }
}

6. Краткое содержание

В ходе этой лабораторной работы вы узнали, как настроить программу Go для использования с Cloud Profiler. Вы также узнали, как собирать, просматривать и анализировать данные о производительности с помощью этого инструмента. Теперь вы можете применить свои новые навыки к реальным сервисам, которые вы используете на Google Cloud Platform.

7. Поздравляем!

Вы узнали, как настроить и использовать Cloud Profiler!

Узнать больше

Лицензия

Эта работа распространяется под лицензией Creative Commons Attribution 2.0 Generic License.