Repository navigation
Conversation
`import SpecialFunctions` made every `using ForwardDiff` load SpecialFunctions and OpenSpecFun_jll, whose `__init__` opens libopenspecfun. In a `juliac --trim=safe` executable that initializer is kept, so every launch pays for a library the program may never call. The Dual methods for SpecialFunctions are now defined in ForwardDiffSpecialFunctionsExt, together with the rules of other modules whose derivative calls SpecialFunctions (`NaNMath.lgamma`, whose derivative is `digamma`). With SpecialFunctions loaded, the set of methods on `Dual` is unchanged.
`Chunk(x)` returns a Union of 13 `Chunk{N}` types, too wide to union-split, so
building the default config is a dynamic dispatch. `gradient(f, x)`,
`jacobian(f, x)`, `jacobian(f!, y, x)` and `hessian(f, x)` infer as `Any` and
fail `juliac --trim=safe`.
With the new `static_default_chunk` preference, calls without a config branch on
the chunk size before building it, with one branch per size `pickchunksize` can
return. Each branch is statically dispatched, so these calls infer concretely and
pass `--trim=safe`, including when made by other packages. The first call then
compiles every chunk size, e.g. a first `hessian(f, rand(9))` takes 3.5 s instead
of 0.5 s, so the preference is off by default and the default path is unchanged.
A new CI job builds and runs a `--trim=safe` executable from `test/trim`, which
sets the preference.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #864 +/- ##
==========================================
- Coverage 91.23% 91.22% -0.01%
==========================================
Files 11 12 +1
Lines 1072 1117 +45
==========================================
+ Hits 978 1019 +41
- Misses 94 98 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
There's no point in having a SpecialFunctions extension - it's a hard dependency of DiffRules and hence the extension is always loaded. One would first need something like JuliaDiff/DiffRules.jl#120 that removes such hard dependencies in DiffRules. |
|
Hi @devmotion . I checked that DiffRules declares SpecialFunctions in [deps] but never imports it, so the extension stays unloaded unless the user loads SpecialFunctions. With this PR In a minimal trimmed program, removing SpecialFunctions hard dep halved the startup time. |
This PR makes ForwardDiff usable under
juliac --trim=safeand removes a library load at startup for programs that never use special functions.Problem
using ForwardDiffalways loads SpecialFunctions and OpenSpecFun_jll. A trimmed executable keepsOpenSpecFun_jll.__init__, so every launch openslibopenspecfun, even when no special function is ever called. In a minimal trimmed program, that roughly doubles startup time.gradient(f, x),jacobian(f, x),jacobian(f!, y, x)andhessian(f, x)infer asAnyforx::Vector{Float64}, so--trim=saferejects them, including when another package makes the call. Only calls with an explicitChunk{N}config compile.Root cause
For (2):
Chunk(x)returns a Union of 13Chunk{N}types. That is too wide to union-split, so building the default config is a dynamic dispatch.Fix
ForwardDiffSpecialFunctionsExtdefines theDualmethods for SpecialFunctions, pluslogabsgammaandgamma_inc, and rules of other modules whose derivative calls SpecialFunctions (today onlyNaNMath.lgamma). User-visible change:NaNMath.lgammaon aDualnow needs SpecialFunctions loaded.static_default_chunkpreference, methods without a config branch on the chunk size before building it, one branch per sizepickchunksizecan return, so they infer concretely. It is off by default because the first call then compiles every chunk size (firsthessian(f, rand(9)): 0.5 s → 3.5 s); with it off, behavior is unchanged. Documented inadvanced.md.Verification
--trim=safeexecutable fromtest/trimwith the preference set, comparing results for n = 3 and 20 with the JIT. Passes locally on Julia 1.14.0-alpha1; without the preference the build fails with unresolved calls tohessian/HessianConfig.