[БЕЗ_ЗВУКА] В этом видео мы рассмотрим применение профилировщика pprof для поиска горячих мест в уже работающей программе под боевой нагрузкой. Профилировщик можно подключить, как обработчики каких-то URL-ов, по которым мы можем снять либо профиль CPU, либо хип-дамп памяти. Выполняется это посредством подключения... импортирования пакета net/http/pprof. Поскольку мы не будем к нему обращаться никак, то нужно написать префикс и использовать пустую переменную. Сам пакет только регистрирует некоторые свои обработчики в глобальном обработчике функций. Вот HandleFunc, вот так. Ну теперь давайте рассмотрим нашу программу. У нас есть какая-то структура, есть обработчик запроса, который просто создает в цикле структуру и после приводит ее к текстовому виду, к строке, и эту строку конкатенирует к общему результату, который потом выводится наверх. Выглядит это вот так. То есть много-много-много каких-то данных. Теперь для того, чтобы снять хип-дамп и профиль CPU, нам нужно дернуть URL-ы debug/pprof/heap и debug/pprof/profile для снятия CPU, при этом мы можем указать в параметре seconds, за сколько времени мы хотим снять профиль CPU. Далее, используя pprof, саму команду, мы можем построить .svg файл по полученным результатам. То есть это уже не обязательно делать под боевой нагрузкой. Мы можем снять хип-дамп или профиль процессора, увести это туда, где никакой нагрузки нет, где это не затронет пользователей, и там уже строить, и там уже искать узкие места. Давайте попробуем это запустить. Итак, у меня запущена программа, у меня готов к запуску файл, сейчас я подам нагрузку через стандартную утилиту ab для бенчмарка. Запускаем, снимаем стек-трейс. Три, четыре, пять. Отлично. Стек-трейс поднят. Нагрузку отключили. Теперь давайте посмотрим, как выглядят результаты, и что мы там можем найти. Итак, у нас есть несколько файлов. cpu — это профиль CPU, и mem_ao — это количество аллоцированных объектов, то есть в каких местах у нас были аллокации. Начнем с cpu. Давайте для начала начнем наш профиль, наш обработчик. Ага, вот он: main.handle. Он по CPU работал 3,76 секунды. И что он делал? То есть далее мы можем посмотреть, куда ушло от него время. Итак, 3,59 секунды ушло на Sprintf, и 2,97 секунды ушло на конкатенацию строк. Sprintf — что он делает, Sprintf? Он внутри, ему нужно пройти через interface и через reflect и привести это к строке. То есть это как раз CPU-intensive, CPU нагрузка. А concatstring, ну, давайте посмотрим на наш код. Вот concatstring. То есть нам надо выделить строку, добавить ее еще к строке, то есть каждый раз у нас получается аллокация. Это достаточно тяжелая операция, если очень долго так делать, это невыгодно. А давайте посмотрим теперь, что по памяти. Найдем наш обработчик. Вот он, main.handle. И где были основные выделения? Вот выделение строки, выделение строки и runtime — это создание новой строки. Теперь Printf, вот основную память ел Printf. Sprintf, вот он тоже выделял строку и внутри он делал структуры из пакета reflect.(*structType).Field для того, чтобы по ним как-то пройтись и посчитать. Ну и тоже он выделял память. То есть используя этот граф, мы можем хотя бы найти, в каких местах, где у нас есть горячие места, их даже визуально видно. То есть это очень мощный инструмент для отладки программ в продакшене без того, чтобы пытаться создать такого рода нагрузку синтетически.