Elixir — это все о производительности. Допустим, у вас есть приложение, работающее на Elixir, но некоторые его части работают не так быстро, как хотелось бы.
Вот тут-то и приходит на помощь профилирование.
Инструменты профилирования обычно позволяют определить частоту и продолжительность вызовов функций и то, на что они тратят свое время.
Erlang имеет в своем распоряжении впечатляющие инструменты профилирования.
В этой заметке мы рассмотрим три инструмента профилирования в Elixir: cprof
, eprof
и fprof
.
- Нужно ли мне профилирование для моего приложения Elixir?
- Профилирование с помощью cprof в Elixir
- eprof Профилирование в Elixir
- Использование fprof в Elixir
- Профиль и сбор показателей вашего приложения Elixir в производстве
- Phoenix Live Dashboard
- AppSignal
- Подведение итогов: Измерьте производительность вашего приложения Elixir с помощью профилирования
Нужно ли мне профилирование для моего приложения Elixir?
Во-первых, я хочу пояснить, что профилирование может и не понадобиться. Как писал Дональд Кнут: «преждевременная оптимизация — корень всех зол». Некоторые операции просто нельзя оптимизировать.
Например, вы хотите получить что-то из вашей базы данных. Вы делаете запрос и получаете ответ. При этом происходит обход сети с некоторыми данными. Если вам нужны все данные, время обхода сети не может быть оптимизировано без перемещения базы данных ближе к приложению.
С этим разобрались, давайте перейдем к вариантам профилирования.
Профилирование с помощью cprof
в Elixir
cprof
подсчитывает количество вызовов каждой функции.
Он не профилирует время, проведенное в этих функциях, поэтому он имеет наименьшие накладные расходы.
Используйте его, когда у вас уже есть оценка времени работы ваших функций. Вы увидите, с какой частотой вызывается каждая функция.
Давайте посмотрим, как с помощью cprof
можно профилировать генератор чисел Фибоначчи.
Вы можете следить за кодом. Он использует алгоритм Rosetta Code для генерации последовательности Фибоначчи.
Войдите в сессию IEx и запустите cprof
следующим образом:
iex(15)> :cprof.start()
19244
iex(16)> FibonacciRosettaCode.list(10)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
iex(17)> :cprof.analyse(FibonacciRosettaCode)
{FibonacciRosettaCode, 79,
[
{{FibonacciRosettaCode, :fibonacci, 3}, 54},
{{FibonacciRosettaCode, :fibonacci, 1}, 11},
{{FibonacciRosettaCode, :"-fun.fibonacci/1-", 1}, 11},
{{FibonacciRosettaCode, :module_info, 1}, 1},
{{FibonacciRosettaCode, :list, 1}, 1},
{{FibonacciRosettaCode, :__info__, 1}, 1}
]}
iex(18)> :cprof.stop()
19266
Сначала запустите cprof
, запустите код, который вы хотите профилировать, а затем вызовите :cprof.analyse/1
для получения статистики для данного модуля. Внутри модуля :cprof
доступно несколько других опций, например, приостановка профилирования с помощью :cprof.pause/0
.
Мы также можем напрямую использовать обертку задачи profile.cprof от Mix.
➜ mix profile.cprof -e "FibonacciRosettaCode.list(10)"
CNT
Total 113
FibonacciRosettaCode 77 <--
FibonacciRosettaCode.fibonacci/3 54
FibonacciRosettaCode.fibonacci/1 11
FibonacciRosettaCode."-fun.fibonacci/1-"/1 11
FibonacciRosettaCode.list/1 1
Enum 24 <--
Enum.reduce_range/5 12
anonymous fn/3 in Enum.map/2 11
Enum.map/2 1
...
Здесь мы видим, что функция fibonacci/3
вызывается чаще всего.
Поэтому, если мы сможем как-то уменьшить количество обращений к этой функции или оптимизировать код внутри нее, мы сможем повысить производительность.
eprof
Профилирование в Elixir
cprof
измеряет количество вызовов, а eprof
измеряет время выполнения (в дополнение к количеству вызовов), проведенное внутри каждой функции.
Он имеет немного больше накладных расходов, чем cprof
. Используйте eprof
, когда вы хотите найти наиболее трудоемкие функции.
Использование очень похоже на cprof
. Вам просто нужно start
профилировщика, запустить код, который вы хотите профилировать, а затем вызвать analyze
для получения результатов.
Давайте проанализируем результаты задачи mix profile.eprof
на нашем примере генератора Фибоначчи.
➜ mix profile.eprof -e "FibonacciRosettaCode.list(10)"
# CALLS % TIME µS/CALL
Total 106 100.0 45 0.42
anonymous fn/0 in :elixir_compiler_1.__FILE__/1 1 0.00 0 0.00
:lists.reverse/1 1 2.22 1 1.00
:lists.reverse/2 1 2.22 1 1.00
FibonacciRosettaCode.fibonacci/1 11 4.44 2 0.18
Enum.map/2 1 4.44 2 2.00
Range.new/2 1 4.44 2 2.00
FibonacciRosettaCode."-fun.fibonacci/1-"/1 11 8.89 4 0.36
FibonacciRosettaCode.list/1 1 8.89 4 4.00
:erlang.apply/2 1 8.89 4 4.00
anonymous fn/3 in Enum.map/2 11 13.33 6 0.55
Enum.reduce_range/5 12 13.33 6 0.50
FibonacciRosettaCode.fibonacci/3 54 28.89 13 0.24
Здесь мы видим, что fibonacci/3
снова является самой трудоемкой частью нашей программы, потребляя 28,89% от общего времени выполнения и 0,24 мкс на вызов.
Кроме того, eprof
также позволяет нам профилировать вызовы функций в различных процессах.
Использование очень простое. Рассмотрим пример:
:eprof.start_profiling([self()])
# Do some work
1..100 |> Enum.each(fn i ->
spawn(fn -> FibonacciRosettaCode.list(i + 1) end)
end)
:eprof.stop_profiling()
:eprof.analyze()
Вам нужно вызвать start_profiling/1
со списком процессов для профилирования.
По умолчанию он также отслеживает любые другие процессы, запущенные из профилируемого процесса.
Когда вы закончите, вызовите stop_profiling
и запустите analyze
, чтобы получить результаты. Они должны выглядеть примерно так, с записью для каждого процесса и процентом времени, которое он был занят:
****** Process <0.150.0> -- 37.26 % of profiled time ***
FUNCTION CALLS % TIME [uS / CALLS]
-------- ----- ------- ---- [----------]
io:request/2 3 0.00 0 [ 0.00]
...
****** Process <0.277.0> -- 0.05 % of profiled time ***
FUNCTION CALLS % TIME [uS / CALLS]
-------- ----- ------- ---- [----------]
'Elixir.FibonacciRosettaCode':fibonacci/3 2 0.00 0 [ 0.00]
'Elixir.FibonacciRosettaCode':fibonacci/1 3 3.03 1 [ 0.33]
'Elixir.FibonacciRosettaCode':'-fun.fibonacci/1-'/1 3 3.03 1 [ 0.33]
...
Самое замечательное в этой программе то, что вы можете запустить ее в производственных системах, получив доступ к удаленной консоли. Просто вызовите start_profiling/1
, подождите некоторое время для обработки запросов или вручную запустите некоторые запросы, которые вы хотите профилировать. Затем вызовите stop_profiling
, а затем analyze
, чтобы получить результаты.
Использование fprof
в Elixir
Последним встроенным инструментом, который вы можете использовать для профилирования приложений, является fprof
. Это комплексный инструмент профилирования, который генерирует файл трассировки, содержащий отмеченные временем записи вызовов функций, событий, связанных с процессом, и данные о сборке мусора.
Затем вы можете передать этот файл трассировки в другие инструменты для тщательной визуализации результатов или использовать :fprof.analyse
, как описано выше, для получения количества функций и времени их выполнения.
Запуск fprof
через IEx довольно сложен по сравнению с запуском cprof
и eprof
, но fprof
позволяет отслеживать несколько процессов.
Вот как можно сгенерировать файл трассировки для всех процессов:
:fprof.start()
:fprof.trace([:start, procs: :all])
# Do some work
1..100 |> Enum.each(fn i ->
spawn(fn -> FibonacciRosettaCode.list(i + 1) end)
end)
:fprof.trace(:stop)
:fprof.profile()
:fprof.analyse(totals: false, dest: 'prof.analysis')
Это генерирует полную трассировку всего, что происходило во время запуска:
Вот полный файл (50 МБ), если вам интересно.
Как и eprof
, fprof
— отличный инструмент для профилирования приложений непосредственно в производстве. Обратите внимание, что это значительно замедлит работу приложения, поэтому будьте готовы к снижению производительности во время профилирования и огромному файлу трассировки.
Итак, теперь у вас есть данные. Если вы чувствуете себя авантюристом, вы можете начать копаться в файле и искать закономерности.
Или вы можете использовать некоторые инструменты для визуализации данных.
Популярным из них является erlgrind
. Он конвертирует файл fprof
в формат cgrind
, который можно открыть внутри KCachegrind
.
Чтобы преобразовать файл, загрузите erlgrind
и затем запустите src/erlgrind profile.fprof
.
В результате будет создан файл prof.cgrind
, который можно открыть, чтобы увидеть графики, подобные этому:
Профиль и сбор показателей вашего приложения Elixir в производстве
eprof
и fprof
могут помочь в профилировании вашего приложения в производстве, но есть еще несколько инструментов, о которых стоит упомянуть — Phoenix Live Dashboard и AppSignal.
Phoenix Live Dashboard
Живая приборная панель Phoenix может обеспечить отличный быстрый обзор метрик. Хотя это не совсем профилировщик, он показывает данные ОС и метрики из событий телеметрии и процессов, среди прочего.
Вот пример приборной панели в действии:
AppSignal
AppSignal — еще один отличный инструмент для сбора данных о производительности (среди прочего). Добавление AppSignal в существующее приложение занимает несколько секунд.
Просто установите зависимость appsignal
и запустите смешанную задачу appsignal.install
с ключом API. Он имеет хороший набор метрик по умолчанию, включая пропускную способность и время отклика для приложения. Вы даже можете настроить минутные зонды для отслеживания пользовательских метрик.
Вот пример приборной панели AppSignal, собирающей данные из образца приложения:
Если вы хотите собрать данные о конкретных блоках кода, которые, по вашему мнению, работают медленно, функция инструментария AppSignal может помочь собрать эти данные. Просто оберните подозрительный код внутри Appsignal.instrument/2
, как показано ниже:
defmodule MyApp.PageController do
use MyApp, :controller
def index(conn, _params) do
custom_function()
render(conn, "index.html")
end
defp custom_function do
Appsignal.instrument("custom_function", fn ->
:timer.sleep(1000)
end)
end
end
Это отобразится на графике событий AppSignal с тегом custom_function
. Затем вы можете изучить это событие в нескольких вызовах и принять обоснованное решение о необходимости оптимизации.
Ознакомьтесь с полным списком возможностей в документации AppSignal.
Подведение итогов: Измерьте производительность вашего приложения Elixir с помощью профилирования
В этом посте мы рассмотрели, как встроенные инструменты, такие как cprof
, eprof
и fprof
, могут помочь собрать информацию о производительности вашего кода даже во время производства.
Мы также рассмотрели пару других инструментов для мониторинга и трассировки приложений: Phoenix Live Dashboard и AppSignal.
До следующего раза, счастливого профилирования!
P.S. Если вы хотите читать посты Elixir Alchemy сразу после их выхода, подпишитесь на нашу рассылку и не пропустите ни одного поста!