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.@profile
— Macro@profile
@profile <expression>
는 주기적으로 백트레이스를 가져오면서 표현식을 실행합니다. 이러한 백트레이스는 내부 백트레이스 버퍼에 추가됩니다.
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
–: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}}
– 보고서에 포함할 작업을 지정합니다. 이는 샘플이 수집되는 작업을 제어하지 않습니다.
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
가 더 나은 선택일 수 있습니다. 기본적으로 스레드 ID 및 작업 ID와 같은 메타데이터가 포함됩니다. 메타데이터를 제거하려면 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}}
이전 프로파일링 실행을 기반으로 특정 함수를 호출한 사람을 확인합니다. 파일 이름(선택적으로 함수가 정의된 줄 번호 범위)을 제공하면 오버로드된 메서드를 구분할 수 있습니다. 반환 값은 호출 수와 호출자에 대한 줄 정보가 포함된 벡터입니다. 선택적으로 retrieve
에서 얻은 백트레이스 data
를 제공할 수 있으며, 그렇지 않으면 현재 내부 프로파일 버퍼가 사용됩니다.
Profile.clear_malloc_data
— Functionclear_malloc_data()
--track-allocation
옵션으로 Julia를 실행할 때 저장된 메모리 할당 데이터를 지웁니다. 테스트할 명령어를 실행하여 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)
힙의 스냅샷을 작성하여 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()
을 통해 나중에 스냅샷을 재구성할 수 있습니다.
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에 의해 생성된 힙 스냅샷을 보는 데 사용할 수 없습니다.