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.