Документация Engee

Профилирование

Профилирование ЦП

Для профилирования ЦП в коде Julia используются два основных подхода.

Применение @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)
...

Активация в ходе выполнения

Пользователь может в любой момент запускать профилирование за фиксированный период времени в уже выполняемых задачах.

Профилирование активируется следующим образом:

  • В MacOS и FreeBSD (платформах на базе BSD): используйте ctrl-t или передайте процессу Julia сигнал SIGINFO, т. е. % kill -INFO $julia_pid.

  • В Linux: передайте процессу Julia сигнал SIGUSR1, т. е. % kill -USR1 $julia_pid.

  • В Windows: в настоящее время не поддерживается.

Сначала отображается одна трассировка стека на момент передачи сигнала, затем собирается профиль за период в одну секунду, а затем выдается отчет о профилировании в следующей точке выхода. Если код не содержит точек выхода, например представляет собой сплошной цикл, то может использоваться момент завершения задачи.

Вы также можете присвоить переменной среды JULIA_PROFILE_PEEK_HEAP_SNAPSHOT значение 1, чтобы автоматически получить моментальный снимок кучи.

julia> foo()
##== Пользователь отправляет активирующий сигнал во время выполнения foo ==##
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()
...

Дополнительная настройка

Длительность профилирования можно изменять с помощью Profile.set_peek_duration.

Отчет о профилировании группируется по потокам и задачам. Чтобы изменить это, передайте в Profile.peek_report[] функцию без аргументов. Т. е. Profile.peek_report[] = () -> Profile.print() позволяет отменить какое-либо группирование. Группирование также может быть изменено во внешнем потребителе данных профилирования.

Справка

# Profile.@profileMacro

@profile

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

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

# Profile.clearFunction

clear()

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

# 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: ограничивает формат :tree глубиной maxdepth.

  • 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}}: указывает задачи, моментальные снимки которых следует включить в отчет. Обратите внимание, что это не определяет, из каких задач осуществляется выборка.

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

Выводит результаты профилирования в поток ввода-вывода (io). Этот вариант используется для анализа результатов, экспортированных предыдущим вызовом retrieve. Необходимо указать вектор обратных трассировок data и словарь строк данных lidict.

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

# Profile.initFunction

init(; n::Integer, delay::Real)

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

# Profile.fetchFunction

fetch(;include_meta = true) -> data

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

# Profile.retrieveFunction

retrieve(; kwargs...) -> data, lidict

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

# Profile.callersFunction

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

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

# Profile.clear_malloc_dataFunction

clear_malloc_data()

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

# Profile.get_peek_durationFunction

get_peek_duration()

Получает длительность профиля peek (в секундах), активируемого с помощью SIGINFO или SIGUSR1 в зависимости от платформы.

# Profile.set_peek_durationFunction

set_peek_duration(t::Float64)

Задает длительность профиля peek (в секундах), активируемого с помощью SIGINFO или SIGUSR1 в зависимости от платформы.

Профилирование памяти

# Profile.Allocs.@profileMacro

Profile.Allocs.@profile [sample_rate=0.0001] 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)

Лучшим способом визуализации на данный момент является пакет PProf.jl, который вызывается с помощью PProf.Allocs.pprof.

Текущая реализация профилировщика выделения записывает не все типы выделения ресурсов. Случаи выделения ресурсов, для которых профилировщик не смог записать тип, представляются с типом Profile.Allocs.UnknownType. Дополнительные сведения о недостающих типах и планах по исправлению ситуации см. здесь: https://github.com/JuliaLang/julia/issues/43688.

Совместимость: Julia 1.8

Профилировщик выделения ресурсов был добавлен в версии Julia 1.8.

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

# Profile.Allocs.clearFunction

Profile.Allocs.clear()

Очищает из памяти всю ранее записанную информацию профилирования выделения ресурсов.

# Profile.Allocs.fetchFunction

Profile.Allocs.fetch()

Получает записанное выделение ресурсов и декодирует его в объекты Julia, пригодные для анализа.

# Profile.Allocs.startFunction

Profile.Allocs.start(sample_rate::Real)

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

# Profile.Allocs.stopFunction

Profile.Allocs.stop()

Останавливает запись выделения ресурсов.

Моментальные снимки кучи

# Profile.take_heap_snapshotFunction

Profile.take_heap_snapshot(io::IOStream, all_one::Bool=false)
Profile.take_heap_snapshot(filepath::String, all_one::Bool=false)
Profile.take_heap_snapshot(all_one::Bool=false)

Записывает моментальный снимок кучи в формате JSON, используемом средством просмотра моментальных снимков кучи в составе инструментов разработчика Chrome (расширение .heapsnapshot), в файл ($pid_$timestamp.heapsnapshot) в текущем каталоге, по указанному пути или в поток ввода-вывода. Если all_one имеет значение true, для каждого объекта указывается единичный размер, что упрощает подсчет объектов. В противном случае указывается фактический размер.

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

julia> using Profile

julia> Profile.take_heap_snapshot("snapshot.heapsnapshot")

Отслеживает и регистрирует объекты Julia в куче. Регистрируются только объекты, известные сборщику мусора Julia. Память, выделенная внешними библиотеками, которыми не управляет сборщик мусора, не включается в моментальный снимок.

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