gf: cache single-dispatch and simple signatures in the call cache
Dynamic calls to builtins and intrinsics (from the interpreter and from
inference's concrete evaluation) went through a full non-leaf typemap
lookup on every call, since their methods have Vararg{Any} signatures
that are never eligible for the leaf-only call cache. The same held for
widened @nospecialize cache entries such as Tuple{typeof(objectid), Any}
or Expr's constructor, which were 82% of the remaining slow-path lookups
during bootstrap and 94% in the dict test suite.
Generalize the call cache in two ways to cover these.
First, add one extra cache slot hashed on the callee value itself,
checked before the callsite-hashed slots: most callees dispatch to a
single cache entry regardless of callsite ("single dispatch"), so this
probe usually hits first, from any callsite - including ones that thrash
the callsite-hashed slots, such as the interpreter's few C callsites.
Entries whose signature is exactly Tuple{typeof(f), Vararg{Any}}
(builtins and intrinsic calls) match every call of f and are accepted
with an inline shape test.
Second, allow cached entries to be non-leaf simple signatures,
re-matched with sig_match_simple via a new jl_typemap_entry_sig_match
helper that mirrors the per-entry logic of jl_typemap_entry_assoc_exact.
An inline pre-rejection test keeps the cost of probing a foreign entry
to a single comparison. Guarded entries are never cached, since a guard
rejection must fall through to the rest of the typemap. Cacheable
entries are inserted both by callee and by callsite so both probe
patterns can find them.
Dynamic dispatch of the affected calls becomes 1.3-3.3x cheaper (===
45ns -> 14ns, add_int 56ns -> 21ns, getfield 63ns -> 23ns, Expr(:call,
:f) 159ns -> 57ns, Core.Typeof 31ns -> 21ns), and ordinary leaf dispatch
of a monomorphic callee gets slightly faster since it now hits the first
probe (+ 14.8ns -> 14.3ns). The one small loser is a callee dispatched
with alternating argument types from a single callsite, which pays one
extra probe (18.4ns -> 20.5ns). Interpreter- and concrete-eval-heavy
work benefits the most: a from-scratch sysimage bootstrap (including
stdlib precompilation) goes from 11m26s to 8m47s (-23%) on this machine,
and compiling the compiler (#62198) and package precompilation see
similar wins.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>