剖析
CPU 剖析
有兩種主要方法可以剖析 julia 程式碼的 CPU
透過 @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
或傳遞SIGINFO
訊號給 julia 程序,例如% kill -INFO $julia_pid
- Linux:傳遞
SIGUSR1
訊號給 julia 程序,例如% kill -USR1 $julia_pid
- Windows:目前不支援。
首先,會在觸發訊號的瞬間顯示單一堆疊追蹤,然後收集 1 秒剖析,接著在下次讓步點產生剖析報告,對於沒有讓步點的程式碼,例如緊密迴圈,讓步點可能會在任務完成時。
選擇性設定環境變數 JULIA_PROFILE_PEEK_HEAP_SNAPSHOT
為 1
,以自動收集 堆疊快照。
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
— 巨集@profile
@profile <expression>
在執行表達式的同時,定期擷取回溯。這些回溯會附加到回溯的內部緩衝區。
Profile
中的方法未匯出,需要呼叫,例如 Profile.print()
。
Profile.clear
— 函數clear()
清除內部緩衝區中任何現有的回溯。
Profile.print
— 函數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}}
– 指定要包含在報告中的快照工作。請注意,這不會控制收集樣本的工作。
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
— 函數init(; n::Integer, delay::Real)
設定回溯之間的延遲
(以秒為單位),以及每個執行緒可以儲存的指令指標數量n
。每個指令指標對應到程式碼的一行;回溯通常包含一個很長的指令指標清單。請注意,每個回溯的指令指標有 6 個空白用於儲存元資料和兩個 NULL 結束標記。目前的設定可以透過呼叫此函數(不帶任何參數)取得,而且每個設定都可以使用關鍵字或依序使用 (n, delay)
獨立設定。
Profile.fetch
— 函數fetch(;include_meta = true) -> data
傳回剖析回溯緩衝區的副本。請注意,data
中的值僅在此機器和目前的階段有意義,因為它取決於 JIT 編譯中使用的精確記憶體位址。此函數主要供內部使用;retrieve
可能對大部分使用者來說是更好的選擇。預設會包含執行緒 ID 和任務 ID 等元資料。將 include_meta
設定為 false
以移除元資料。
Profile.retrieve
— 函數retrieve(; kwargs...) -> data, lidict
以可攜式格式「匯出」剖析結果,傳回所有回溯的集合(data
)和一個字典,將 data
中的(特定於階段的)指令指標對應到儲存檔案名稱、函數名稱和行號的 LineInfo
值。此函數讓您可以儲存剖析結果以供未來分析。
Profile.callers
— 函數callers(funcname, [data, lidict], [filename=<filename>], [linerange=<start:stop>]) -> Vector{Tuple{count, lineinfo}}
給定先前的剖析執行,判斷誰呼叫特定函數。提供檔案名稱(以及函數定義範圍內的行號,可選擇),可以消除重載方法的歧義。傳回值是向量,包含呼叫次數和呼叫者行資訊。可以選擇提供從 retrieve
取得的回溯追蹤 data
;否則,將使用目前的內部剖析緩衝區。
Profile.clear_malloc_data
— 函數clear_malloc_data()
使用 --track-allocation
執行 Julia 時,清除任何儲存的記憶體配置資料。執行要測試的命令(強制 JIT 編譯),然後呼叫 clear_malloc_data
。然後再次執行命令,退出 Julia,並檢查產生的 *.mem
檔案。
Profile.get_peek_duration
— 函數get_peek_duration()
取得剖析「窺視」的持續時間(秒),該「窺視」會透過 SIGINFO
或 SIGUSR1
觸發,具體取決於平台。
Profile.set_peek_duration
— 函數set_peek_duration(t::Float64)
設定剖析「窺視」的持續時間(秒),該「窺視」會透過 SIGINFO
或 SIGUSR1
觸發,具體取決於平台。
記憶體剖析
Profile.Allocs.@profile
— 巨集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)
目前使用 PProf.jl 套件,透過呼叫 PProf.Allocs.pprof
,是視覺化這些內容的最佳方式。
配置剖析器的目前實作不會擷取所有配置的類型。剖析器無法擷取其類型的配置會表示為類型 Profile.Allocs.UnknownType
。
您可以在此處進一步了解遺失的類型和改善此問題的計畫:https://github.com/JuliaLang/julia/issues/43688。
配置剖析器已新增至 Julia 1.8。
Profile.Allocs
中的方法未匯出,且需要呼叫,例如 Profile.Allocs.fetch()
。
Profile.Allocs.clear
— 函式Profile.Allocs.clear()
從記憶體中清除所有先前剖析的配置資訊。
Profile.Allocs.fetch
— 函式Profile.Allocs.fetch()
擷取已記錄的配置,並將其解碼為可分析的 Julia 物件。
Profile.Allocs.start
— 函式Profile.Allocs.start(sample_rate::Real)
開始記錄配置,取樣率為給定值。取樣率 1.0 會記錄所有內容;0.0 則不會記錄任何內容。
Profile.Allocs.stop
— 函式Profile.Allocs.stop()
停止記錄配置。
堆快照
Profile.take_heap_snapshot
— 函式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; dir::String)
將堆快照寫入 JSON 格式檔案(預設為目前的目錄中的 $pid_$timestamp.heapsnapshot
)(或如果目前的目錄無法寫入,則寫入 tempdir),或寫入 dir
(如果已提供)或指定的完整檔案路徑,或 IO 串流。
如果 all_one
為 true,則將每個物件的大小報告為 1,以便輕鬆計算。否則,報告實際大小。
Profile
中的方法未匯出,需要呼叫,例如 Profile.take_heap_snapshot()
。
julia> using Profile
julia> Profile.take_heap_snapshot("snapshot.heapsnapshot")
追蹤並記錄堆上的 Julia 物件。這只會記錄 Julia 垃圾收集器已知的物件。由外部函式庫配置、未由垃圾收集器管理的記憶體不會顯示在快照中。
產生的堆快照檔案可以上傳到 Chrome Devtools 以供檢視。更多資訊,請參閱 Chrome Devtools 文件。