Profiling
CPU Profiling
Il existe deux approches principales pour le profilage CPU du code Julia :
Via @profile
Là où le profilage est activé pour un appel donné via le macro @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
Les tâches qui sont déjà en cours peuvent également être profilées pendant une période de temps fixe à tout moment déclenché par l'utilisateur.
Pour déclencher le profilage :
- MacOS et FreeBSD (plateformes basées sur BSD) : Utilisez
ctrl-t
ou envoyez un signalSIGINFO
au processus julia, c'est-à-dire% kill -INFO $julia_pid
- Linux : Envoyez un signal
SIGUSR1
au processus julia c'est-à-dire% kill -USR1 $julia_pid
- Windows : Pas actuellement pris en charge.
Tout d'abord, une seule trace de pile au moment où le signal a été lancé est affichée, puis un profil de 1 seconde est collecté, suivi du rapport de profil au prochain point de yield, qui peut être à l'achèvement de la tâche pour du code sans points de yield, par exemple des boucles serrées.
Optionnellement, définissez la variable d'environnement JULIA_PROFILE_PEEK_HEAP_SNAPSHOT
à 1
pour également collecter automatiquement un 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
La durée du profilage peut être ajustée via Profile.set_peek_duration
Le rapport de profil est divisé par fil et tâche. Passez une fonction sans argument à Profile.peek_report[]
pour remplacer cela. c'est-à-dire Profile.peek_report[] = () -> Profile.print()
pour supprimer tout regroupement. Cela pourrait également être remplacé par un consommateur de données de profil externe.
Reference
Profile.@profile
— Macro@profile
@profile <expression>
exécute votre expression tout en prenant des traces de pile périodiques. Celles-ci sont ajoutées à un tampon interne de traces de pile.
Les méthodes dans Profile
ne sont pas exportées et doivent être appelées par exemple comme Profile.print()
.
Profile.clear
— Functionclear()
Efface tous les backtraces existants du tampon interne.
Profile.print
— Functionprint([io::IO = stdout,] [data::Vector = fetch()], [lidict::Union{LineInfoDict, LineInfoFlatDict} = getdict(data)]; kwargs...)
Imprime les résultats de profilage dans io
(par défaut, stdout
). Si vous ne fournissez pas de vecteur data
, le tampon interne des backtraces accumulés sera utilisé.
Les arguments de mot-clé peuvent être n'importe quelle combinaison de :
format
– Détermine si les backtraces sont imprimés avec (par défaut,:tree
) ou sans (:flat
) indentation indiquant la structure de l'arbre.C
– Sitrue
, les backtraces du code C et Fortran sont affichés (normalement, ils sont exclus).combine
– Sitrue
(par défaut), les pointeurs d'instruction sont fusionnés correspondant à la même ligne de code.maxdepth
– Limite la profondeur supérieure àmaxdepth
dans le format:tree
.sortedby
– Contrôle l'ordre dans le format:flat
.:filefuncline
(par défaut) trie par la ligne source,:count
trie par ordre du nombre d'échantillons collectés, et:overhead
trie par le nombre d'échantillons encourus par chaque fonction elle-même.groupby
– Contrôle le regroupement sur les tâches et les threads, ou aucun regroupement. Les options sont:none
(par défaut),:thread
,:task
,[:thread, :task]
, ou[:task, :thread]
où les deux dernières fournissent un regroupement imbriqué.noisefloor
– Limite les frames qui dépassent le seuil de bruit heuristique de l'échantillon (s'applique uniquement au format:tree
). Une valeur suggérée à essayer pour cela est 2.0 (la valeur par défaut est 0). Ce paramètre cache les échantillons pour lesquelsn <= noisefloor * √N
, oùn
est le nombre d'échantillons sur cette ligne, etN
est le nombre d'échantillons pour le callee.mincount
– Limite l'impression uniquement à ces lignes avec au moinsmincount
occurrences.recur
– Contrôle la gestion de la récursion dans le format:tree
.:off
(par défaut) imprime l'arbre normalement.:flat
compresse à la place toute récursion (par ip), montrant l'effet approximatif de la conversion de toute auto-récursion en un itérateur.:flatc
fait la même chose mais inclut également l'effondrement des frames C (peut faire des choses étranges autour dejl_apply
).threads::Union{Int,AbstractVector{Int}}
– Spécifiez quels threads inclure des instantanés dans le rapport. Notez que cela ne contrôle pas quels threads les échantillons sont collectés (qui peuvent également avoir été collectés sur une autre machine).tasks::Union{Int,AbstractVector{Int}}
– Spécifiez quelles tâches inclure des instantanés dans le rapport. Notez que cela ne contrôle pas quelles tâches les échantillons sont collectés à l'intérieur.
Les arguments de mot-clé groupby
, threads
, et tasks
ont été introduits dans Julia 1.8.
Le profilage sur Windows est limité au thread principal. D'autres threads n'ont pas été échantillonnés et ne figureront pas dans le rapport.
print([io::IO = stdout,] data::Vector, lidict::LineInfoDict; kwargs...)
Imprime les résultats de profilage dans io
. Cette variante est utilisée pour examiner les résultats exportés par un appel précédent à retrieve
. Fournissez le vecteur data
des backtraces et un dictionnaire lidict
des informations de ligne.
Voir Profile.print([io], data)
pour une explication des arguments de mot-clé valides.
Profile.init
— Functioninit(; n::Integer, delay::Real)
Configurez le delay
entre les backtraces (mesuré en secondes), et le nombre n
de pointeurs d'instruction qui peuvent être stockés par thread. Chaque pointeur d'instruction correspond à une seule ligne de code ; les backtraces consistent généralement en une longue liste de pointeurs d'instruction. Notez que 6 espaces pour les pointeurs d'instruction par backtrace sont utilisés pour stocker des métadonnées et deux marqueurs de fin NULL. Les paramètres actuels peuvent être obtenus en appelant cette fonction sans arguments, et chacun peut être défini indépendamment en utilisant des mots-clés ou dans l'ordre (n, delay)
.
Profile.fetch
— Functionfetch(;include_meta = true) -> data
Renvoie une copie du tampon des traces de profil. Notez que les valeurs dans data
n'ont de signification que sur cette machine dans la session actuelle, car elles dépendent des adresses mémoire exactes utilisées dans la compilation JIT. Cette fonction est principalement destinée à un usage interne ; retrieve
peut être un meilleur choix pour la plupart des utilisateurs. Par défaut, des métadonnées telles que threadid et taskid sont incluses. Définissez include_meta
sur false
pour supprimer les métadonnées.
Profile.retrieve
— Functionretrieve(; kwargs...) -> data, lidict
"Exports" les résultats de profilage dans un format portable, retournant l'ensemble de toutes les traces de retour (data
) et un dictionnaire qui associe les pointeurs d'instruction (spécifiques à la session) dans data
à des valeurs LineInfo
qui stockent le nom de fichier, le nom de fonction et le numéro de ligne. Cette fonction vous permet de sauvegarder les résultats de profilage pour une analyse future.
Profile.callers
— Functioncallers(funcname, [data, lidict], [filename=<filename>], [linerange=<start:stop>]) -> Vector{Tuple{count, lineinfo}}
Étant donné une exécution de profilage précédente, déterminez qui a appelé une fonction particulière. Fournir le nom de fichier (et éventuellement, la plage de numéros de ligne sur laquelle la fonction est définie) vous permet de désambiguïser une méthode surchargée. La valeur retournée est un vecteur contenant un compte du nombre d'appels et des informations de ligne sur l'appelant. On peut optionnellement fournir un data
de backtrace obtenu à partir de retrieve
; sinon, le tampon de profilage interne actuel est utilisé.
Profile.clear_malloc_data
— Functionclear_malloc_data()
Efface toutes les données d'allocation de mémoire stockées lors de l'exécution de julia avec --track-allocation
. Exécutez la ou les commandes que vous souhaitez tester (pour forcer la compilation JIT), puis appelez clear_malloc_data
. Ensuite, exécutez à nouveau votre ou vos commandes, quittez Julia et examinez les fichiers *.mem
résultants.
Profile.get_peek_duration
— Functionget_peek_duration()
Obtenez la durée en secondes du profil "peek" qui est déclenché via SIGINFO
ou SIGUSR1
, selon la plateforme.
Profile.set_peek_duration
— Functionset_peek_duration(t::Float64)
Définir la durée en secondes du profil "peek" qui est déclenché via SIGINFO
ou SIGUSR1
, selon la plateforme.
Memory profiling
Profile.Allocs.@profile
— MacroProfile.Allocs.@profile [sample_rate=0.1] expr
Profilez les allocations qui se produisent pendant expr
, retournant à la fois le résultat et une structure AllocResults.
Un taux d'échantillonnage de 1.0 enregistrera tout ; 0.0 n'enregistrera rien.
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)
Voir le tutoriel de profilage dans la documentation Julia pour plus d'informations.
Les versions antérieures de Julia ne pouvaient pas capturer les types dans tous les cas. Dans les versions antérieures de Julia, si vous voyez une allocation de type Profile.Allocs.UnknownType
, cela signifie que le profileur ne sait pas quel type d'objet a été alloué. Cela se produisait principalement lorsque l'allocation provenait de code généré par le compilateur. Voir issue #43688 pour plus d'infos.
Depuis Julia 1.11, toutes les allocations devraient avoir un type signalé.
Le profileur d'allocation a été ajouté dans Julia 1.8.
Les méthodes dans Profile.Allocs
ne sont pas exportées et doivent être appelées par exemple comme Profile.Allocs.fetch()
.
Profile.Allocs.clear
— FunctionProfile.Allocs.clear()
Effacer toutes les informations d'allocation précédemment profilées de la mémoire.
Profile.Allocs.print
— FunctionProfile.Allocs.print([io::IO = stdout,] [data::AllocResults = fetch()]; kwargs...)
Imprime les résultats de profilage dans io
(par défaut, stdout
). Si vous ne fournissez pas de vecteur data
, le tampon interne des backtraces accumulées sera utilisé.
Voir Profile.print
pour une explication des arguments de mot-clé valides.
Profile.Allocs.fetch
— FunctionProfile.Allocs.fetch()
Récupérez les allocations enregistrées et décodez-les en objets Julia qui peuvent être analysés.
Profile.Allocs.start
— FunctionProfile.Allocs.start(sample_rate::Real)
Commence l'enregistrement des allocations avec le taux d'échantillonnage donné. Un taux d'échantillonnage de 1.0 enregistrera tout ; 0.0 n'enregistrera rien.
Profile.Allocs.stop
— FunctionProfile.Allocs.stop()
Arrêter l'enregistrement des allocations.
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)
Écrivez un instantané du tas, dans le format JSON attendu par le visualiseur d'instantanés de tas des outils de développement Chrome (.heapsnapshot extension) dans un fichier ($pid_$timestamp.heapsnapshot
) dans le répertoire courant par défaut (ou tempdir si le répertoire courant n'est pas accessible en écriture), ou dans dir
si donné, ou le chemin de fichier complet donné, ou un flux IO.
Si all_one
est vrai, alors signalez la taille de chaque objet comme un, afin qu'ils puissent être facilement comptés. Sinon, signalez la taille réelle.
Si streaming
est vrai, nous allons diffuser les données de l'instantané dans quatre fichiers, en utilisant filepath comme préfixe, pour éviter de devoir conserver l'ensemble de l'instantané en mémoire. Cette option doit être utilisée pour tout paramètre où votre mémoire est limitée. Ces fichiers peuvent ensuite être réassemblés en appelant Profile.HeapSnapshot.assemble_snapshot(), ce qui peut être fait hors ligne.
REMARQUE : Nous recommandons fortement de définir streaming=true pour des raisons de performance. Reconstruire l'instantané à partir des parties nécessite de conserver l'ensemble de l'instantané en mémoire, donc si l'instantané est volumineux, vous pouvez manquer de mémoire lors de son traitement. Le streaming vous permet de reconstruire l'instantané hors ligne, après que votre charge de travail ait terminé de s'exécuter. Si vous essayez de collecter un instantané avec streaming=false (la valeur par défaut, pour des raisons de compatibilité ascendante) et que votre processus est tué, notez que cela enregistrera toujours les parties dans le même répertoire que votre chemin de fichier fourni, afin que vous puissiez toujours reconstruire l'instantané après coup, via assemble_snapshot()
.
Les méthodes dans Profile
ne sont pas exportées et doivent être appelées par exemple comme Profile.take_heap_snapshot()
.
julia> using Profile
julia> Profile.take_heap_snapshot("snapshot.heapsnapshot")
Trace et enregistre les objets Julia sur le tas. Cela n'enregistre que les objets connus du ramasse-miettes Julia. La mémoire allouée par des bibliothèques externes non gérées par le ramasse-miettes ne s'affichera pas dans l'instantané.
Pour éviter un OOM lors de l'enregistrement du snapshot, nous avons ajouté une option de streaming pour diffuser le snapshot de tas dans quatre fichiers,
julia> using Profile
julia> Profile.take_heap_snapshot("snapshot"; streaming=true)
où "snapshot" est le chemin d'accès en tant que préfixe pour les fichiers générés.
Une fois les fichiers de snapshot générés, ils peuvent être assemblés hors ligne avec la commande suivante :
julia> using Profile
julia> Profile.HeapSnapshot.assemble_snapshot("snapshot", "snapshot.heapsnapshot")
Le fichier de snapshot de tas résultant peut être téléchargé dans les outils de développement Chrome pour être visualisé. Pour plus d'informations, voir le chrome devtools docs. Une alternative pour analyser les snapshots de tas Chromium est avec l'extension VS Code ms-vscode.vscode-js-profile-flame
.
Les instantanés de tas de Firefox sont d'un format différent, et Firefox ne peut actuellement pas être utilisé pour visualiser les instantanés de tas générés par Julia.