I want to present here again and perhaps more clearly, the concept of context dispatch , or more precisely resolving multiple dispatch based on some context.
I believe this can contribute to precompilation and load times issues, and may further the understanding of how to create a reusable binary cache for the compiled code and where and how to store such data.
The building blocks:
A Context is a set of modules.
The Context of module M is the set of all modules that module M depends upon.
Extension: for the purpose of multiple dispatch , a module M can declare that it extends function
funfrom module B. This in “Julian” terms is referred to as module M specifically imports function
funfrom module B. Any call to
M.funwill redirect to
B.funitself will dispatch a to a specific method considering also the definitions of
B.funin its method table.
Context Call: calling a function
funwith a given context , the method table for
funwill be determined by the modules that are present in the context (and that extend
fun), the context is propagated recursively down the AST and every internal call is a context call.
There was previously some debate whether context call is feasible … I made some heroic attempts to prove so,
but @jrevels attempts were even more heroic, coming up with Cassette.jl which made my previous attempts redundant. kudos.
Given a certain fixed context, it is quite clear that we can cache all compilation outputs for functions called within that context as long as we don’t define additional functions in any of the dependent modules.
It is also quite clear that the context of module
Main is not cacheable, since it is dynamic and changing from invocation to invocation.
The current state of affairs in Julia is that every call is equivalent to a context call with the context of Main. Which makes binary caching very hard if not impossible.
All solutions should incorporate some heuristics for “narrowing” the context, since any context that is not Main is probably cacheable.
Narrow context based on types, calling function
Mwith arguments types t1,t2…
will narrow the context to the context of M if all types are instance-able from the context of M
Post compilation narrowing: log the hierarchy of modules “visited” while compiling a function, log the compiled code in the smallest context that includes all visited modules. Then in a later stage if we encounter a function call from context M and there exist a binary cache for that function in M or in any of the modules inside Context of M … use the binary cache instead of compiling.
I feel this is a complicated subject better addressed in small pieces.
using Cassette.jl I can easily set up a POC for any kind of context dispatch rules, I will do so if there is interest from the devs and community.