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.@profileMacro
@profile

@profile <выражение> выполняет ваше выражение, периодически делая обратные трассировки. Они добавляются во внутренний буфер обратных трассировок.

source

Методы в Profile не экспортируются и должны вызываться, например, как Profile.print().

Profile.clearFunction
clear()

Очистите любые существующие трассировки из внутреннего буфера.

source
Profile.printFunction
print([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}} – Укажите, какие задачи включить в снимки в отчете. Обратите внимание, что это не контролирует, в каких задачах собираются образцы.
Julia 1.8

Ключевые аргументы groupby, threads и tasks были введены в Julia 1.8.

Note

Профилирование в Windows ограничено основным потоком. Другие потоки не были выбраны для выборки и не будут отображаться в отчете.

source
print([io::IO = stdout,] data::Vector, lidict::LineInfoDict; kwargs...)

Выводит результаты профилирования в io. Этот вариант используется для изучения результатов, экспортированных предыдущим вызовом к retrieve. Укажите вектор data с обратными трассировками и словарь lidict с информацией о строках.

Смотрите Profile.print([io], data) для объяснения допустимых именованных аргументов.

source
Profile.initFunction
init(; n::Integer, delay::Real)

Настройте delay между обратными трассировками (измеряется в секундах) и число n указателей инструкций, которые могут храниться на поток. Каждый указатель инструкции соответствует одной строке кода; обратные трассировки обычно состоят из длинного списка указателей инструкций. Обратите внимание, что 6 пробелов для указателей инструкций на каждую обратную трассировку используются для хранения метаданных и двух NULL маркеров конца. Текущие настройки можно получить, вызвав эту функцию без аргументов, и каждую из них можно установить независимо, используя ключевые слова или в порядке (n, delay).

source
Profile.fetchFunction
fetch(;include_meta = true) -> data

Возвращает копию буфера профилей обратных трассировок. Обратите внимание, что значения в data имеют смысл только на этой машине в текущей сессии, поскольку они зависят от точных адресов памяти, используемых при JIT-компиляции. Эта функция в основном предназначена для внутреннего использования; retrieve может быть лучшим выбором для большинства пользователей. По умолчанию включены метаданные, такие как threadid и taskid. Установите include_meta в false, чтобы удалить метаданные.

source
Profile.retrieveFunction
retrieve(; kwargs...) -> data, lidict

"Экспортирует" результаты профилирования в переносимом формате, возвращая набор всех обратных трассировок (data) и словарь, который сопоставляет (специфичные для сессии) указатели инструкций в data со значениями LineInfo, которые хранят имя файла, имя функции и номер строки. Эта функция позволяет вам сохранять результаты профилирования для будущего анализа.

source
Profile.callersFunction
callers(funcname, [data, lidict], [filename=<filename>], [linerange=<start:stop>]) -> Vector{Tuple{count, lineinfo}}

Учитывая предыдущий запуск профилирования, определите, кто вызвал определенную функцию. Указание имени файла (и, при желании, диапазона номеров строк, в которых определена функция) позволяет вам различать перегруженные методы. Возвращаемое значение - это вектор, содержащий количество вызовов и информацию о строке вызывающего. Можно дополнительно предоставить стек вызовов data, полученный из retrieve; в противном случае используется текущий внутренний буфер профилирования.

source
Profile.clear_malloc_dataFunction
clear_malloc_data()

Очищает любые сохраненные данные о выделении памяти при запуске julia с --track-allocation. Выполните команду(ы), которые вы хотите протестировать (чтобы заставить JIT-компиляцию), затем вызовите clear_malloc_data. Затем выполните ваши команды снова, выйдите из Julia и проверьте полученные файлы *.mem.

source
Profile.get_peek_durationFunction
get_peek_duration()

Получите продолжительность в секундах профиля "peek", который вызывается через SIGINFO или SIGUSR1, в зависимости от платформы.

source
Profile.set_peek_durationFunction
set_peek_duration(t::Float64)

Установите продолжительность в секундах профиля "peek", который активируется через SIGINFO или SIGUSR1, в зависимости от платформы.

source

Memory profiling

Profile.Allocs.@profileMacro
Profile.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 1.11

Более старые версии Julia не могли захватывать типы во всех случаях. В более старых версиях Julia, если вы видите аллокацию типа Profile.Allocs.UnknownType, это означает, что профайлер не знает, какой тип объекта был выделен. Это в основном происходило, когда аллокация происходила из сгенерированного кода, созданного компилятором. Смотрите issue #43688 для получения дополнительной информации.

Начиная с Julia 1.11, все аллокации должны иметь указанный тип.

Julia 1.8

Профайлер аллокаций был добавлен в Julia 1.8.

source

Методы в Profile.Allocs не экспортируются и должны вызываться, например, как Profile.Allocs.fetch().

Profile.Allocs.clearFunction
Profile.Allocs.clear()

Очистите всю ранее профилированную информацию об аллокациях из памяти.

source
Profile.Allocs.printFunction
Profile.Allocs.print([io::IO = stdout,] [data::AllocResults = fetch()]; kwargs...)

Выводит результаты профилирования в io (по умолчанию, stdout). Если вы не предоставите вектор data, будет использован внутренний буфер накопленных обратных трассировок.

Смотрите Profile.print для объяснения допустимых именованных аргументов.

source
Profile.Allocs.fetchFunction
Profile.Allocs.fetch()

Получите зафиксированные аллокации и декодируйте их в объекты Julia, которые можно анализировать.

source
Profile.Allocs.startFunction
Profile.Allocs.start(sample_rate::Real)

Начать запись аллокаций с заданной частотой выборки. Частота выборки 1.0 будет записывать все; 0.0 не будет записывать ничего.

source

Heap Snapshots

Profile.take_heap_snapshotFunction
Profile.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().

source

Методы в 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.