Reference

SnoopCompile.@snoopiMacro
inf_timing = @snoopi commands
inf_timing = @snoopi tmin=0.0 commands

Execute commands while snooping on inference. Returns an array of (t, linfo) tuples, where t is the amount of time spent infering linfo (a MethodInstance).

Methods that take less time than tmin will not be reported.

source
SnoopCompile.@snoopcMacro
@snoopc "compiledata.csv" begin
    # Commands to execute, in a new process
end

causes the julia compiler to log all functions compiled in the course of executing the commands to the file "compiledata.csv". This file can be used for the input to SnoopCompile.read.

source
SnoopCompile.@snooprMacro
list = @snoopr expr

Capture method cache invalidations triggered by evaluating expr. list is a sequence of invalidated Core.MethodInstances together with "explanations," consisting of integers (encoding depth) and strings (documenting the source of an invalidation).

Unless you are working at a low level, you essentially always want to pass list directly to invalidation_trees.

Extended help

list is in a format where the "reason" comes after the items. Method deletion results in the sequence

[zero or more (mi, "invalidate_mt_cache") pairs..., zero or more (depth1 tree, loctag) pairs..., method, loctag] with loctag = "jl_method_table_disable"

where mi means a MethodInstance. depth1 means a sequence starting at depth=1.

Method insertion results in the sequence

[zero or more (depth0 tree, sig) pairs..., same info as with delete_method except loctag = "jl_method_table_insert"]
source
SnoopCompile.parcelFunction

pc = parcel(calls; subst=[], blacklist=[]) assigns each compile statement to the module that owns the function. Perform string substitution via subst=["Module1"=>"Module2"], and omit functions in particular modules with blacklist=["Module3"]. On output, pc[:Module2] contains all the precompiles assigned to Module2.

Use SnoopCompile.write(prefix, pc) to generate a series of files in directory prefix, one file per module.

source
SnoopCompile.writeFunction
write(prefix::AbstractString, pc::Dict; always::Bool = false)

Write each modules' precompiles to a separate file. If always is true, the generated function will always run the precompile statements when called, otherwise the statements will only be called during package precompilation.

source
SnoopCompile.readFunction

SnoopCompile.read("compiledata.csv") reads the log file produced by the compiler and returns the functions as a pair of arrays. The first array is the amount of time required to compile each function, the second is the corresponding function + types. The functions are sorted in order of increasing compilation time. (The time does not include the cost of nested compiles.)

source
SnoopCompile.format_userimgFunction

pc = format_userimg(calls; subst=[], blacklist=[]) generates precompile directives intended for your base/userimg.jl script. Use SnoopCompile.write(filename, pc) to create a file that you can include into userimg.jl.

source
SnoopCompile.timesumFunction
timesum(snoop)

Calculates and prints the total time measured by a snoop macro.

It is used inside @snoopibench. Julia can cache inference results so to measure the effect of adding _precompile() sentences generated by snoopi to your package, use the @snoopi_bench. This benchmark measures inference time taken during loading and running of a package.

Examples

using SnoopCompile
data = @snoopi begin
    include(joinpath(dirname(dirname(pathof(MatLang))),"test","runtests.jl"))
end;
println(timesum(data));

Manual Benchmark (withtout using @snoopi_bench)

  • dev your package

  • comment the precompile part of your package (include() and _precompile_())

  • run the following benchmark

  • restart Julia

  • uncomment the precompile part of your package (include() and _precompile_())

  • run the following benchmark

  • restart Julia

Benchmark

using SnoopCompile

println("Package load time:")
loadSnoop = @snoopi using MatLang

timesum(loadSnoop)

println("Running Examples/Tests:")
runSnoop = @snoopi begin
    using MatLang
    include(joinpath(dirname(dirname(pathof(MatLang))),"test","runtests.jl"))
end

timesum(runSnoop)
source
SnoopCompile.invalidation_treesFunction
trees = invalidation_trees(list)

Parse list, as captured by @snoopr, into a set of invalidation trees, where parents nodes were called by their children.

Example

julia> f(x::Int)  = 1
f (generic function with 1 method)

julia> f(x::Bool) = 2
f (generic function with 2 methods)

julia> applyf(container) = f(container[1])
applyf (generic function with 1 method)

julia> callapplyf(container) = applyf(container)
callapplyf (generic function with 1 method)

julia> c = Any[1]
1-element Array{Any,1}:
 1

julia> callapplyf(c)
1

julia> trees = invalidation_trees(@snoopr f(::AbstractFloat) = 3)
1-element Array{SnoopCompile.MethodInvalidations,1}:
 insert f(::AbstractFloat) in Main at REPL[36]:1 invalidated:
   mt_backedges: 1: signature Tuple{typeof(f),Any} triggered MethodInstance for applyf(::Array{Any,1}) (1 children) more specific

See the documentation for further details.

source
SnoopCompile.filtermodFunction
thinned = filtermod(module, trees::AbstractVector{MethodInvalidations})

Select just the cases of invalidating a method defined in module.

source
SnoopCompile.findcallerFunction
invs = findcaller(method::Method, trees)

Find a path through trees that reaches method. Returns a single MethodInvalidations object.

Examples

Suppose you know that loading package SomePkg triggers invalidation of f(data). You can find the specific source of invalidation as follows:

f(data)                             # run once to force compilation
m = @which f(data)
using SnoopCompile
trees = invalidation_trees(@snoopr using SomePkg)
invs = findcaller(m, trees)

If you don't know which method to look for, but know some operation that has had added latency, you can look for methods using @snoopi. For example, suppose that loading SomePkg makes the next using statement slow. You can find the source of trouble with

julia> using SnoopCompile

julia> trees = invalidation_trees(@snoopr using SomePkg);

julia> tinf = @snoopi using SomePkg            # this second `using` will need to recompile code invalidated above
1-element Array{Tuple{Float64,Core.MethodInstance},1}:
 (0.08518409729003906, MethodInstance for require(::Module, ::Symbol))

julia> m = tinf[1][2].def
require(into::Module, mod::Symbol) in Base at loading.jl:887

julia> findcaller(m, trees)
insert ==(x, y::SomeType) in SomeOtherPkg at /path/to/code:100 invalidated:
   backedges: 1: superseding ==(x, y) in Base at operators.jl:83 with MethodInstance for ==(::Symbol, ::Any) (16 children) more specific
source