剖析

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_SNAPSHOT1,以自動收集 堆疊快照

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.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}} – 指定要包含在報告中的快照工作。請注意,這不會控制收集樣本的工作。

Julia 1.8

groupbythreadstasks 關鍵字參數在 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()

取得剖析「窺視」的持續時間(秒),該「窺視」會透過 SIGINFOSIGUSR1 觸發,具體取決於平台。

來源
Profile.set_peek_duration函數
set_peek_duration(t::Float64)

設定剖析「窺視」的持續時間(秒),該「窺視」會透過 SIGINFOSIGUSR1 觸發,具體取決於平台。

來源

記憶體剖析

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

配置剖析器已新增至 Julia 1.8。

來源

Profile.Allocs 中的方法未匯出,且需要呼叫,例如 Profile.Allocs.fetch()

Profile.Allocs.fetch函式
Profile.Allocs.fetch()

擷取已記錄的配置,並將其解碼為可分析的 Julia 物件。

來源
Profile.Allocs.start函式
Profile.Allocs.start(sample_rate::Real)

開始記錄配置,取樣率為給定值。取樣率 1.0 會記錄所有內容;0.0 則不會記錄任何內容。

來源

堆快照

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 文件