Profiling

CPU Profiling

CPU 프로파일링 줄리아 코드를 위한 두 가지 주요 접근 방식이 있습니다:

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: julia 프로세스에 SIGUSR1 신호를 전달합니다. 즉, % kill -USR1 $julia_pid
  • Windows: 현재 지원되지 않음.

먼저, 신호가 발생한 순간의 단일 스택 추적이 표시되고, 그 다음 1초 프로파일이 수집되며, 다음 양보 지점에서 프로파일 보고서가 제공됩니다. 이는 양보 지점이 없는 코드(예: 타이트 루프)의 경우 작업 완료 시점일 수 있습니다.

Optionally set environment variable JULIA_PROFILE_PEEK_HEAP_SNAPSHOT to 1 to also automatically collect a 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 <expression>는 주기적으로 백트레이스를 가져오면서 표현식을 실행합니다. 이러한 백트레이스는 내부 백트레이스 버퍼에 추가됩니다.

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) 출력되는지를 결정합니다.
  • Ctrue인 경우, C 및 Fortran 코드의 백트레이스가 표시됩니다(정상적으로는 제외됨).
  • combinetrue인 경우(기본값), 동일한 코드 줄에 해당하는 명령 포인터가 병합됩니다.
  • 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은 대신 재귀를 압축하여(아이피 기준) 자기 재귀를 반복자로 변환하는 대략적인 효과를 보여줍니다. :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가 더 나은 선택일 수 있습니다. 기본적으로 스레드 ID 및 작업 ID와 같은 메타데이터가 포함됩니다. 메타데이터를 제거하려면 include_metafalse로 설정하십시오.

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}}

이전 프로파일링 실행을 기반으로 특정 함수를 호출한 사람을 확인합니다. 파일 이름(선택적으로 함수가 정의된 줄 번호 범위)을 제공하면 오버로드된 메서드를 구분할 수 있습니다. 반환 값은 호출 수와 호출자에 대한 줄 정보가 포함된 벡터입니다. 선택적으로 retrieve에서 얻은 백트레이스 data를 제공할 수 있으며, 그렇지 않으면 현재 내부 프로파일 버퍼가 사용됩니다.

source
Profile.clear_malloc_dataFunction
clear_malloc_data()

--track-allocation 옵션으로 Julia를 실행할 때 저장된 메모리 할당 데이터를 지웁니다. 테스트할 명령어를 실행하여 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)

힙의 스냅샷을 작성하여 Chrome Devtools Heap Snapshot 뷰어에서 기대하는 JSON 형식으로 파일($pid_$timestamp.heapsnapshot)에 저장합니다. 기본적으로 현재 디렉토리에 저장되며(현재 디렉토리에 쓸 수 없는 경우 tempdir에 저장됨), 또는 주어진 경우 dir에 저장되거나 주어진 전체 파일 경로 또는 IO 스트림에 저장됩니다.

all_one이 true인 경우, 모든 객체의 크기를 1로 보고하여 쉽게 계산할 수 있도록 합니다. 그렇지 않으면 실제 크기를 보고합니다.

streaming이 true인 경우, 스냅샷 데이터를 네 개의 파일로 스트리밍하여 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")

힙에 있는 줄리아 객체의 추적 및 기록. 이는 줄리아 가비지 컬렉터에 의해 알려진 객체만 기록합니다. 가비지 컬렉터에 의해 관리되지 않는 외부 라이브러리에 의해 할당된 메모리는 스냅샷에 나타나지 않습니다.

스냅샷을 기록하는 동안 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 DevTools에 업로드되어 볼 수 있습니다. 자세한 내용은 chrome devtools docs를 참조하세요. Chromium 힙 스냅샷을 분석하는 또 다른 방법은 VS Code 확장 ms-vscode.vscode-js-profile-flame을 사용하는 것입니다.

Firefox 힙 스냅샷은 다른 형식이며, 현재 Firefox는 Julia에 의해 생성된 힙 스냅샷을 보는 데 사용할 수 없습니다.