Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Visualization of Julia profiling data

License

NotificationsYou must be signed in to change notification settings

timholy/ProfileView.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build StatuscodecovPkgEval

NOTE: Jupyter/IJulia and SVG support has migrated to theProfileSVG package.

Introduction

This package contains tools for visualizing and interacting with profiling data collectedwithJulia's built-in samplingprofiler. Itcan be helpful for getting a big-picture overview of the majorbottlenecks in your code, and optionally highlights lines that triggergarbage collection as potential candidates for optimization.

This type of plot is known as aflamegraph.The main logic is handled by theFlameGraphs package; this package is just a visualization front-end.

Compared to other flamegraph viewers, ProfileView adds interactivity features, such as:

  • zoom, pan for exploring large flamegraphs
  • right-clicking to take you to the source code for a particular statement
  • analyzing inference problems viacode_warntype for specific, user-selected calls

These features are described in detail below.

Installation

Within Julia, use thepackage manager:

using PkgPkg.add("ProfileView")

Tutorial: usage and visual interpretation

To demonstrate ProfileView, first we have to collect some profilingdata. Here's a simple test function for demonstration:

functionprofile_test(n)for i=1:n        A=randn(100,100,20)        m=maximum(A)        Am=mapslices(sum, A; dims=2)        B= A[:,:,5]        Bsort=mapslices(sort, B; dims=1)        b=rand(100)        C= B.*bendendusing ProfileView@profviewprofile_test(1)# run once to trigger compilation (ignore this one)@profviewprofile_test(10)

@profview f(args...) is just shorthand forProfile.clear(); @profile f(args...); ProfileView.view().(These commands require that you first sayusing Profile, the Julia profiling standard library.)

If you use ProfileView from VSCode you'll get an errorUndefVarError: @profview not defined.This is because VSCode defines its own@profview, which conflicts with ProfileView's.Fix it by usingProfileView.@profview.

If you're following along, you may see something like this:

ProfileView

(Note that collected profiles can vary by Julia version and from run-to-run, so don't be alarmedif you get something different.)This plot is a visual representation of thecall graph of the code that you just profiled.The "root" of the tree is at the bottom; if you move your mouse along the long horizontalbar at the bottom, you'll see a tooltip that's something like

boot.jl, eval: 330

This refers to one of Julia's own source files,base/boot.jl.eval is the name of the function being executed, and330 is the line number of the file.This is the function that evaluated yourprofile_test(10) command that you typed at the REPL.(Indeed, to reduce the amount of internal "overhead" in the flamegraph, some of these internals are truncated; see thenorepl option ofFlameGraphs.flamegraph.)If you move your mouse upwards, you'll then see bars corresponding to the function(s) you ran with@profview (in this case,profile_test).Thus, the vertical axis represents nesting depth: bars lie on top of the bars that called them.

The horizontal axis represents the amount of time (more precisely, thenumber of backtraces) spent at each line. The row at which the singlelong bar breaks up into multiple different-colored bars correspondsto the execution of different lines fromprofile_test.The fact thatthey are all positioned on top of the lower peach-colored bar means that allof these lines are called by the same "parent" function. Within ablock of code, they are sorted in order of increasing line number, tomake it easier for you to compare to the source code.

From this visual representation, we can very quickly learn severalthings about this function:

  • On the right side, you see a stack of calls to functions insort.jl.This is because sorting is implemented using recursion (functions that call themselves).

  • mapslices(sum, A; dims=2) is considerably more expensive (the corresponding bar is horizontally wider) thanmapslices(sort, B; dims=1). This is because it has to process moredata.

Color Coding

  • Red: is reserved for functioncalls that have to be resolved at run-time.Because run-time dispatch (aka, dynamic dispatch, run-time method lookup, ora virtual call) often has a significantimpact on performance, ProfileView highlights the problematic call in red. It'sworth noting that some red is unavoidable; for example, the REPL can'tpredict in advance the return types from what users type at theprompt, and so the bottomeval call is red.Red bars are problematic only when they account for a sizablefraction of the top of a call stack, as only in such cases are they likely to bethe source of a significant performance bottleneck.In the image above, can see thatmapslices relied on run-time dispatch;from the absence of pastel-colored bars above much of the red, wemight guess that this made a substantialcontribution to its total run time.(Your version of Julia may show different results.)SeeSolving type-inference problems belowfor tips on how to efficiently diagnose the nature of the problem.

  • Yellow: indicates a site of garbage collection, which can betriggered at a site of memory allocation. You may find that such bars lead you to lineswhose performance can be improved by reducing the amount of temporary memory allocatedby your program. One common example is to consider using@views(A[:, i] .* v) insteadofA[:, i] .* v; the latter creates a new column-vector fromA, whereas the formerjust creates a reference to it.Julia'smemory profilermay provide much more information about the usage of memory in your program.

GUI features

Customizable defaults:

Some default settings can be changed and retained across settings through aLocalPreferences.toml file that is added to the active environment.

  • Default color theme: The default is:light.Alternatively:dark can be set.UseProfileView.set_theme!(:dark) to change the default.

  • Default graph type: The default is:flame which displays from the bottom up.Alternatively:icicle displays from the top down.UseProfileView.set_graphtype!(:icicle) to change the default.

Gtk Interface

  • Ctrl-q and Ctrl-w close the window. You can also useProfileView.closeall() to close all windows opened by ProfileView.

  • Left-clicking on a bar will cause information about this line to beprinted in the REPL. This can be a convenient way to "mark" linesfor later investigation.

  • Right-clicking on a bar calls theedit() function to open the linein an editor. (On a trackpad, use a 2-fingered tap.)

  • CTRL-clicking and dragging will zoom in on a specific region of the image. Youcan also control the zoom level with CTRL-scroll (or CTRL-swipe up/down).

    CTRL-double-click to restore the full view.

  • You can pan the view by clicking and dragging, or by scrolling yourmouse/trackpad (scroll=vertical, SHIFT-scroll=horizontal).

  • The toolbar at the top contains two icons to load and save profiledata, respectively. Clicking the save icon will prompt you for afilename; you should use extension*.jlprof for any file you save.LaunchingProfileView.view(nothing) opens a blankwindow, which you can populate with saved data by clicking on the"open" icon.

  • After clicking on a bar, you can typewarntype_last and see theresult ofcode_warntype for the call represented by that bar.

  • ProfileView.view(windowname="method1") allows you to name your window,which can help avoid confusion when opening several ProfileView windowssimultaneously.

  • On Julia 1.8 ProfileView.view(expand_tasks=true) creates one tab per task.Expanding by thread is on by default and can be disabled withexpand_threads=false.

NOTE: ProfileView does not support the old JLD-based*.jlprof files anymore.Use the format provided by FlameGraphs v0.2 and higher.

Solving type-inference problems

Cthulhu.jl is a powerful tool fordiagnosing problems of type inference. Let's do a simple demo:

functionprofile_test_sort(n, len=100000)for i=1:n        list= []for _in1:lenpush!(list,rand())endsort!(list)endendjulia>profile_test_sort(1)# to force compilationjulia>@profviewprofile_test_sort(10)

Notice that there are lots of individual red bars (sort! is recursive) along the toprow of the image. To determine the nature of the inference problem(s) in a red bar, left-click on itand then enter

julia>using Cthulhujulia>descend_clicked()

You may see something like this:

ProfileView

You can see the source code of the running method, with "problematic" type-inferenceresults highlighted in red. (By default, non-problematic type inference results aresuppressed, but you can toggle their display with'h'.)

For this example, you can see that objects extracted fromv have typeAny: that's because inprofile_test_sort, we createdlist aslist = [], which makes it aVector{Any};in this case, a better option might belist = Float64[]. Notice that thecause of the performanceproblem is quite far-removed from the place where it manifests, because it's only whenthe low-level operations required bysort! get underway that the consequence of our choiceof container type become an issue. Often it's necessary to "chase" these performance issuesbackwards to a caller; for that,ascend_clicked() can be useful:

julia>ascend_clicked()Choose a callfor analysis (q to quit):>partition!(::Vector{Any},::Int64,::Int64,::Int64,::Base.Order.ForwardOrdering,::Vector{Any},::Bool,::Vector{Any},::Int64)#_sort!#25(::Vector{Any}, ::Int64, ::Bool, ::Bool, ::typeof(Base.Sort._sort!), ::Vector{Any}, ::Base.Sort.ScratchQuickSort{Missing, Missing, Base.Sort.Insertiokwcall(::NamedTuple{(:t, :offset, :swap, :rev), Tuple{Vector{Any}, Int64, Bool, Bool}},::typeof(Base.Sort._sort!),::Vector{Any},::Base.Sort.ScratchQuickSo#_sort!#25(::Vector{Any}, ::Int64, ::Bool, ::Bool, ::typeof(Base.Sort._sort!), ::Vector{Any}, ::Base.Sort.ScratchQuickSort{Missing, Missing, Base.Sort.Inse#_sort!#25(::Nothing, ::Nothing, ::Bool, ::Bool, ::typeof(Base.Sort._sort!), ::Vector{Any}, ::Base.Sort.ScratchQuickSort{Missing, Missing, Base.Sort.Insert_sort!(::Vector{Any},::Base.Sort.ScratchQuickSort{Missing, Missing, Base.Sort.InsertionSortAlg},::Base.Order.ForwardOrdering,::NamedTuple{(:scratch,:_sort!(::Vector{Any},::Base.Sort.StableCheckSorted{Base.Sort.ScratchQuickSort{Missing, Missing, Base.Sort.InsertionSortAlg}},::Base.Order.ForwardOrde_sort!(::Vector{Any},::Base.Sort.IsUIntMappable{Base.Sort.Small{40, Base.Sort.InsertionSortAlg, Base.Sort.CheckSorted{Base.Sort.ComputeExtrema{Base._sort!(::Vector{Any},::Base.Sort.IEEEFloatOptimization{Base.Sort.IsUIntMappable{Base.Sort.Small{40, Base.Sort.InsertionSortAlg, Base.Sort.CheckSorv_sort!(::Vector{Any},::Base.Sort.Small{10, Base.Sort.InsertionSortAlg, Base.Sort.IEEEFloatOptimization{Base.Sort.IsUIntMappable{Base.Sort.Small{

This is an interactive menu showing each "callee" above the "caller": use the up and down arrows to pick a call todescend into. If you scroll to the bottomyou'll see theprofile_test_sort call that triggered the whole cascade.

You can also see type-inference results without using Cthulhu: just enter

julia> warntype_clicked()

at the REPL. You'll see the result of Julia'scode_warntype for the call you clicked on.

These commands all useProfileView.clicked[], which stores a stackframe entry for the most recently clickedbar.

Command-line options

Theview command has the following syntax:

function view([fcolor,] data = Profile.fetch(); lidict = nothing, C = false, fontsize = 14, kwargs...)

Here is the meaning of the different arguments:

  • fcolor optionally allows you to control the scheme used to selectbar color. This can be quite extensively customized; seeFlameGraphs for details.

  • data is the vector containing backtraces. You can useusing Profile; data1 = copy(Profile.fetch()); Profile.clear() to store and examine resultsfrom multiple profile runs simultaneously.

  • lidict is a dictionary containing "line information." This is obtained together withdata fromusing Profile; data, lidict = Profile.retrieve(). Computinglidict isthe slow step in displaying profile data, so callingretrieve can speed up repeatedvisualizations of the same data.

  • C is a flag controlling whether lines corresponding to C and Fortrancode are displayed. (Internally, ProfileView uses the informationfrom C backtraces to learn about garbage-collection and todisambiguate the call graph).

  • fontsize controls the size of the font displayed as a tooltip.

  • expand_threads controls whether a page is created for each thread (requires julia 1.8, enabled by default)

  • expand_tasks controls whether a page is shown for each task (requires julia 1.8, off by default)

  • graphtype::Symbol = :default controls how the graph is shown.:flame displays from the bottom up,:icicledisplays from the top down. The default is:flame which can be changed via e.g.ProfileView.set_graphtype!(:icicle).

These are the main options, but there are others; see FlameGraphs for more details.

Source locations & Revise (new in ProfileView 0.5.3)

Profiling andRevise are natural partners,as together they allow you to iteratively improve the performance of your code.If you use Revise and are tracking the source files (either as a package or withincludet),the source locations (file and line number) reported by ProfileViewwill match the current code at the time the window is created.

About

Visualization of Julia profiling data

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors29

Languages


[8]ページ先頭

©2009-2025 Movatter.jp