Profiling
CPU Profiling
Существует два основных подхода к профилированию CPU кода на Julia:
Via @profile
Где профилирование включено для данного вызова через макрос @profile
.
julia> using Profile
julia> @profile foo()
julia> Profile.print()
Overhead ╎ [+additional indent] Count File:Line; Function
=========================================================
╎147 @Base/client.jl:506; _start()
╎ 147 @Base/client.jl:318; exec_options(opts::Base.JLOptions)
...
Triggered During Execution
Задачи, которые уже выполняются, также могут быть профилированы в течение фиксированного периода времени в любое время, инициированное пользователем.
Чтобы запустить профилирование:
- MacOS и FreeBSD (платформы на базе BSD): Используйте
ctrl-t
или передайте сигналSIGINFO
процессу julia, т.е.% kill -INFO $julia_pid
- Linux: Отправьте сигнал
SIGUSR1
процессу julia, т.е.% kill -USR1 $julia_pid
- Windows: В настоящее время не поддерживается.
Сначала показывается один стек вызовов в момент, когда был сгенерирован сигнал, затем собирается профиль за 1 секунду, после чего следует отчет профиля в следующей точке выхода, которая может быть в момент завершения задачи для кода без точек выхода, например, в плотных циклах.
Опционально установите переменную окружения JULIA_PROFILE_PEEK_HEAP_SNAPSHOT
в 1
, чтобы также автоматически собирать heap snapshot.
julia> foo()
##== the user sends a trigger while foo is running ==##
load: 2.53 cmd: julia 88903 running 6.16u 0.97s
======================================================================================
Information request received. A stacktrace will print followed by a 1.0 second profile
======================================================================================
signal (29): Information request: 29
__psynch_cvwait at /usr/lib/system/libsystem_kernel.dylib (unknown line)
_pthread_cond_wait at /usr/lib/system/libsystem_pthread.dylib (unknown line)
...
======================================================================
Profile collected. A report will print if the Profile module is loaded
======================================================================
Overhead ╎ [+additional indent] Count File:Line; Function
=========================================================
Thread 1 Task 0x000000011687c010 Total snapshots: 572. Utilization: 100%
╎147 @Base/client.jl:506; _start()
╎ 147 @Base/client.jl:318; exec_options(opts::Base.JLOptions)
...
Thread 2 Task 0x0000000116960010 Total snapshots: 572. Utilization: 0%
╎572 @Base/task.jl:587; task_done_hook(t::Task)
╎ 572 @Base/task.jl:879; wait()
...
Customization
Длительность профилирования можно настроить с помощью Profile.set_peek_duration
Отчет профиля разбит по потокам и задачам. Передайте функцию без аргументов в Profile.peek_report[]
, чтобы переопределить это. т.е. Profile.peek_report[] = () -> Profile.print()
, чтобы убрать любое группирование. Это также может быть переопределено внешним потребителем данных профиля.
Reference
Profile.@profile
— Macro@profile
@profile <выражение>
выполняет ваше выражение, периодически делая обратные трассировки. Они добавляются во внутренний буфер обратных трассировок.
Методы в Profile
не экспортируются и должны вызываться, например, как Profile.print()
.
Profile.clear
— Functionclear()
Очистите любые существующие трассировки из внутреннего буфера.
Profile.print
— Functionprint([io::IO = stdout,] [data::Vector = fetch()], [lidict::Union{LineInfoDict, LineInfoFlatDict} = getdict(data)]; kwargs...)
Выводит результаты профилирования в io
(по умолчанию, stdout
). Если вы не предоставите вектор data
, будет использован внутренний буфер накопленных трассировок.
Ключевые аргументы могут быть любым сочетанием:
format
– Определяет, будут ли трассировки выводиться с (по умолчанию,:tree
) или без (:flat
) отступов, указывающих на структуру дерева.C
– Еслиtrue
, трассировки из кода C и Fortran отображаются (обычно они исключаются).combine
– Еслиtrue
(по умолчанию), указатели инструкций объединяются, если они соответствуют одной и той же строке кода.maxdepth
– Ограничивает глубину вышеmaxdepth
в формате:tree
.sortedby
– Управляет порядком в формате:flat
.:filefuncline
(по умолчанию) сортирует по строке исходного кода,:count
сортирует по количеству собранных образцов, а:overhead
сортирует по количеству образцов, понесенных каждой функцией.groupby
– Управляет группировкой по задачам и потокам или без группировки. Опции::none
(по умолчанию),:thread
,:task
,[:thread, :task]
или[:task, :thread]
, где последние две обеспечивают вложенную группировку.noisefloor
– Ограничивает кадры, которые превышают эвристический уровень шума образца (применяется только к формату:tree
). Рекомендуемое значение для этого – 2.0 (по умолчанию 0). Этот параметр скрывает образцы, для которыхn <= noisefloor * √N
, гдеn
– количество образцов в этой строке, аN
– количество образцов для вызываемой функции.mincount
– Ограничивает вывод только теми строками, в которых как минимумmincount
вхождений.recur
– Управляет обработкой рекурсии в формате:tree
.:off
(по умолчанию) выводит дерево как обычно.:flat
вместо этого сжимает любую рекурсию (по ip), показывая приблизительный эффект преобразования любой саморекурсии в итератор.:flatc
делает то же самое, но также включает сворачивание кадров C (может вести себя странно вокругjl_apply
).threads::Union{Int,AbstractVector{Int}}
– Укажите, какие потоки включить в снимки в отчете. Обратите внимание, что это не контролирует, на каких потоках собираются образцы (которые также могли быть собраны на другой машине).tasks::Union{Int,AbstractVector{Int}}
– Укажите, какие задачи включить в снимки в отчете. Обратите внимание, что это не контролирует, в каких задачах собираются образцы.
Ключевые аргументы groupby
, threads
и tasks
были введены в Julia 1.8.
Профилирование в Windows ограничено основным потоком. Другие потоки не были выбраны для выборки и не будут отображаться в отчете.
print([io::IO = stdout,] data::Vector, lidict::LineInfoDict; kwargs...)
Выводит результаты профилирования в io
. Этот вариант используется для изучения результатов, экспортированных предыдущим вызовом к retrieve
. Укажите вектор data
с обратными трассировками и словарь lidict
с информацией о строках.
Смотрите Profile.print([io], data)
для объяснения допустимых именованных аргументов.
Profile.init
— Functioninit(; n::Integer, delay::Real)
Настройте delay
между обратными трассировками (измеряется в секундах) и число n
указателей инструкций, которые могут храниться на поток. Каждый указатель инструкции соответствует одной строке кода; обратные трассировки обычно состоят из длинного списка указателей инструкций. Обратите внимание, что 6 пробелов для указателей инструкций на каждую обратную трассировку используются для хранения метаданных и двух NULL маркеров конца. Текущие настройки можно получить, вызвав эту функцию без аргументов, и каждую из них можно установить независимо, используя ключевые слова или в порядке (n, delay)
.
Profile.fetch
— Functionfetch(;include_meta = true) -> data
Возвращает копию буфера профилей обратных трассировок. Обратите внимание, что значения в data
имеют смысл только на этой машине в текущей сессии, поскольку они зависят от точных адресов памяти, используемых при JIT-компиляции. Эта функция в основном предназначена для внутреннего использования; retrieve
может быть лучшим выбором для большинства пользователей. По умолчанию включены метаданные, такие как threadid и taskid. Установите include_meta
в false
, чтобы удалить метаданные.
Profile.retrieve
— Functionretrieve(; kwargs...) -> data, lidict
"Экспортирует" результаты профилирования в переносимом формате, возвращая набор всех обратных трассировок (data
) и словарь, который сопоставляет (специфичные для сессии) указатели инструкций в data
со значениями LineInfo
, которые хранят имя файла, имя функции и номер строки. Эта функция позволяет вам сохранять результаты профилирования для будущего анализа.
Profile.callers
— Functioncallers(funcname, [data, lidict], [filename=<filename>], [linerange=<start:stop>]) -> Vector{Tuple{count, lineinfo}}
Учитывая предыдущий запуск профилирования, определите, кто вызвал определенную функцию. Указание имени файла (и, при желании, диапазона номеров строк, в которых определена функция) позволяет вам различать перегруженные методы. Возвращаемое значение - это вектор, содержащий количество вызовов и информацию о строке вызывающего. Можно дополнительно предоставить стек вызовов data
, полученный из retrieve
; в противном случае используется текущий внутренний буфер профилирования.
Profile.clear_malloc_data
— Functionclear_malloc_data()
Очищает любые сохраненные данные о выделении памяти при запуске julia с --track-allocation
. Выполните команду(ы), которые вы хотите протестировать (чтобы заставить JIT-компиляцию), затем вызовите clear_malloc_data
. Затем выполните ваши команды снова, выйдите из Julia и проверьте полученные файлы *.mem
.
Profile.get_peek_duration
— Functionget_peek_duration()
Получите продолжительность в секундах профиля "peek", который вызывается через SIGINFO
или SIGUSR1
, в зависимости от платформы.
Profile.set_peek_duration
— Functionset_peek_duration(t::Float64)
Установите продолжительность в секундах профиля "peek", который активируется через SIGINFO
или SIGUSR1
, в зависимости от платформы.
Memory profiling
Profile.Allocs.@profile
— MacroProfile.Allocs.@profile [sample_rate=0.1] expr
Профилируйте аллокации, которые происходят во время expr
, возвращая как результат, так и структуру AllocResults.
Частота выборки 1.0 будет записывать все; 0.0 не будет записывать ничего.
julia> Profile.Allocs.@profile sample_rate=0.01 peakflops()
1.03733270279065e11
julia> results = Profile.Allocs.fetch()
julia> last(sort(results.allocs, by=x->x.size))
Profile.Allocs.Alloc(Vector{Any}, Base.StackTraces.StackFrame[_new_array_ at array.c:127, ...], 5576)
Смотрите учебник по профилированию в документации Julia для получения дополнительной информации.
Более старые версии Julia не могли захватывать типы во всех случаях. В более старых версиях Julia, если вы видите аллокацию типа Profile.Allocs.UnknownType
, это означает, что профайлер не знает, какой тип объекта был выделен. Это в основном происходило, когда аллокация происходила из сгенерированного кода, созданного компилятором. Смотрите issue #43688 для получения дополнительной информации.
Начиная с Julia 1.11, все аллокации должны иметь указанный тип.
Профайлер аллокаций был добавлен в Julia 1.8.
Методы в Profile.Allocs
не экспортируются и должны вызываться, например, как Profile.Allocs.fetch()
.
Profile.Allocs.clear
— FunctionProfile.Allocs.clear()
Очистите всю ранее профилированную информацию об аллокациях из памяти.
Profile.Allocs.print
— FunctionProfile.Allocs.print([io::IO = stdout,] [data::AllocResults = fetch()]; kwargs...)
Выводит результаты профилирования в io
(по умолчанию, stdout
). Если вы не предоставите вектор data
, будет использован внутренний буфер накопленных обратных трассировок.
Смотрите Profile.print
для объяснения допустимых именованных аргументов.
Profile.Allocs.fetch
— FunctionProfile.Allocs.fetch()
Получите зафиксированные аллокации и декодируйте их в объекты Julia, которые можно анализировать.
Profile.Allocs.start
— FunctionProfile.Allocs.start(sample_rate::Real)
Начать запись аллокаций с заданной частотой выборки. Частота выборки 1.0 будет записывать все; 0.0 не будет записывать ничего.
Profile.Allocs.stop
— FunctionProfile.Allocs.stop()
Остановить запись аллокаций.
Heap Snapshots
Profile.take_heap_snapshot
— FunctionProfile.take_heap_snapshot(filepath::String, all_one::Bool=false, streaming=false)
Profile.take_heap_snapshot(all_one::Bool=false; dir::String, streaming=false)
Запишите снимок кучи в формате JSON, ожидаемом просмотрщиком снимков кучи Chrome Devtools (.heapsnapshot), в файл ($pid_$timestamp.heapsnapshot
) в текущем каталоге по умолчанию (или в tempdir, если текущий каталог нельзя записать), или в dir
, если он указан, или по указанному полному пути к файлу, или в поток IO.
Если all_one
истинно, то сообщите размер каждого объекта как один, чтобы их можно было легко подсчитать. В противном случае сообщите фактический размер.
Если streaming
истинно, мы будем передавать данные снимка в четыре файла, используя filepath в качестве префикса, чтобы избежать необходимости держать весь снимок в памяти. Этот параметр следует использовать в любых настройках, где ваша память ограничена. Эти файлы затем можно будет собрать, вызвав Profile.HeapSnapshot.assemble_snapshot(), что можно сделать в оффлайне.
ПРИМЕЧАНИЕ: Мы настоятельно рекомендуем установить streaming=true по причинам производительности. Восстановление снимка из частей требует удержания всего снимка в памяти, поэтому, если снимок большой, вы можете столкнуться с нехваткой памяти во время его обработки. Передача позволяет вам восстановить снимок в оффлайне, после завершения вашей нагрузки. Если вы попытаетесь собрать снимок с streaming=false (по умолчанию, для обратной совместимости), и ваш процесс будет завершен, обратите внимание, что это всегда сохранит части в том же каталоге, что и указанный вами путь к файлу, так что вы все равно сможете восстановить снимок позже, через assemble_snapshot()
.
Методы в Profile
не экспортируются и должны вызываться, например, как Profile.take_heap_snapshot()
.
julia> using Profile
julia> Profile.take_heap_snapshot("snapshot.heapsnapshot")
Отслеживает и записывает объекты Julia в куче. Это записывает только объекты, известные сборщику мусора Julia. Память, выделенная внешними библиотеками, не управляемыми сборщиком мусора, не будет отображаться в снимке.
Чтобы избежать OOM при записи снимка, мы добавили опцию потоковой передачи для вывода снимка кучи в четыре файла,
julia> using Profile
julia> Profile.take_heap_snapshot("snapshot"; streaming=true)
где "snapshot" является префиксом для сгенерированных файлов.
Как только файлы снимков будут сгенерированы, их можно собрать в оффлайн-режиме с помощью следующей команды:
julia> using Profile
julia> Profile.HeapSnapshot.assemble_snapshot("snapshot", "snapshot.heapsnapshot")
Результирующий файл снимка кучи можно загрузить в инструменты разработчика Chrome для просмотра. Для получения дополнительной информации см. chrome devtools docs. Альтернативой для анализа снимков кучи Chromium является расширение VS Code ms-vscode.vscode-js-profile-flame
.
Снимки кучи Firefox имеют другой формат, и в настоящее время Firefox не может использоваться для просмотра снимков кучи, сгенерированных Julia.