SnoopCompile 1.0

I’m pleased to announce the 1.0 release of SnoopCompile, a package that can be used to diagnose and sometimes reduce package startup latencies (a.k.a., “time to first plot”).

Version 1.0 brings a couple of new features:

  • the ability to “automate” precompile-file generation via @snoopi. This makes @snoopi, rather than the older @snoopc, the go-to tool of the package.
  • better documentation. Among other things, the new documentation walks you through how to tell which precompile statements are “working” and which ones aren’t, allowing you to streamline your precompile files.
51 Likes

Thanks Tim!

1 Like

Does SnoopCompile filter out methods that depend on gensymmed names that might not be valid in other versions / builds of julia? In that case, is there any way to keep them,in case you are making your own sysimage for example ? (One of the major contributions to the improved startup in Julia 1.0 was due to changes in the build process that allowed keeping these gensymmed statements)

@snoopi returns everything; it’s just SnoopCompile.parcel that’s implements filtering. As you can see, for @snoopi the implementation of parcel is almost trivial, so we could easily tweak it if you think there are gains to be had.

2 Likes

What’s the support for keyword argument functions like?
Using @snoopi created some statements like:

precompile(Tuple{PaddedMatrices.var"#mul_tn_quote##kw",NamedTuple{(:negative, :force_inline),Tuple{Bool,Bool}},typeof(PaddedMatrices.mul_tn_quote),Int64,Symbol,Int64,Type,Bool,Symbol,Symbol,Int64})

Which worked on the system/Julia version I called it with, but (obviously not) on Julia versions predating the var_str macro, but IIRC I got a function-not-defined error on a different Julia version as well.
I’d have to double check tomorrow.

It reduced the compilation of a test function from 39 seconds to 37.5 seconds. I need to do a lot of work to get that better. Part of my problem is that I don’t have a real mental model for what takes time.
Obviously generated functions don’t help, since both the generated function and the function that is generated need to get compiled.

I also had lots of statements like:

precompile(Tuple{PaddedMatrices.var"##s115#407",Any,Any,Any})

Which did not produce errors when tested locally. But, I’m not sure where exactly they come from, or how likely they’re to work across Julia versions/builds, so I commented all of these out.

Should parcel have filtered these names out?

Anything that breaks across Julia versions is a bug, so please report it (with reproducer) as an issue in SnoopCompile.

Obviously generated functions don’t help, since both the generated function and the function that is generated need to get compiled.

If you have essentially limitless type combinations, yes, it’s hopeless. But in limited cases it should work. Demo:

julia> @generated function mygen(x::T) where T
           Tbigger = T == Float32 ? Float64 : BigFloat
           :(convert($Tbigger, x))
       end
mygen (generic function with 1 method)

julia> using SnoopCompile

julia> tinf = @snoopi begin
           mygen(1.0f0)
           mygen(1.0)
       end
3-element Array{Tuple{Float64,Core.MethodInstance},1}:
 (0.00042700767517089844, MethodInstance for #s1#3(::Any, ::Any, ::Any))
 (0.0005278587341308594, MethodInstance for mygen(::Float64))           
 (0.003690004348754883, MethodInstance for mygen(::Float32))            

That #s1#3 is the generator for mygen.

Does that include Julia-master?
I just verified a case where @snoopi created on master (and working there) errors on 1.3.

ERROR: LoadError: UndefVarError: ##s41#17 not defined

Absolutely. The precompile files need to work robustly across versions.