Документация 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, чтобы автоматически получить 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()
...

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

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

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

Справка

@profile

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

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

clear()

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

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

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

Именованные аргументы groupby, threads и tasks появились в Julia 1.8.

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


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

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

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

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

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

fetch(;include_meta = true) -> data

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

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

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

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

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

clear_malloc_data()

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

get_peek_duration()

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

set_peek_duration(t::Float64)

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

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

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, это означает, что профилировщику неизвестно, какой тип объекта был выделен. В основном это происходило, когда выделение осуществлялось из сформированного компилятором кода. Дополнительные сведения см. в проблеме № 43688.

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

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

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

Profile.Allocs.clear()

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

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

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

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

Profile.Allocs.fetch()

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

Profile.Allocs.start(sample_rate::Real)

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

Profile.Allocs.stop()

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

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

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 (расширение .heapsnapshot), в файл ($pid_$timestamp.heapsnapshot) в текущем каталоге по умолчанию (или tempdir, если текущий каталог недоступен для записи), в dir, если указано, по указанному полному пути к файлу или в поток ввода-вывода.

Если all_one имеет значение true, для каждого объекта указывается единичный размер, что упрощает подсчет объектов. В противном случае указывается фактический размер.

Если streaming имеет значение true, мы выполняем потоковую передачу данных моментального снимка в четыре файла, используя 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. Память, выделенная внешними библиотеками, которыми не управляет сборщик мусора, не включается в моментальный снимок.

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

julia> using Profile

julia> Profile.take_heap_snapshot("snapshot"; streaming=true)

где snapshot — это путь к файлу в качестве префикса для сгенерированных файлов.

Созданные файлы моментальных снимков можно собрать в автономном режиме с помощью следующей команды:

julia> using Profile

julia> Profile.HeapSnapshot.assemble_snapshot("snapshot", "snapshot.heapsnapshot")

где snapshot — это путь к файлу в качестве префикса для сгенерированных файлов.

Созданные файлы моментальных снимков можно собрать в автономном режиме с помощью следующей команды: