Skip to content

precompile: don't re-infer image-compiled callees when generating a pkgimage - #62934

Closed
IanButterworth wants to merge 2 commits into
JuliaLang:masterfrom
IanButterworth:ib/pkgimage-skip-image-callees
Closed

precompile: don't re-infer image-compiled callees when generating a pkgimage#62934
IanButterworth wants to merge 2 commits into
JuliaLang:masterfrom
IanButterworth:ib/pkgimage-skip-image-callees

Conversation

@IanButterworth

@IanButterworth IanButterworth commented Aug 31, 2026

Copy link
Copy Markdown
Member

Seems when generating pkgimages we're re-interring stuff that's already in loaded images. Developed with Fable, passed review by Sol.

Claude:


Since #59361, pkgimage worklists pass through typeinf_ext_toplevel, where compile! recursively enqueues each :invoke target and requests its source. Sysimage source IR is no longer retained (#58172, #58662), so this needlessly re-infers image callees; master then discards the result under external linkage (JL_CI_FLAGS_FROM_IMAGE, #60031), while 1.13's older emit path additionally emits some of it as redundant copies in the image. This change skips those callees when building untrimmed package images.

Codegen continues to call skipped callees through their image fptr. Trimmed and system images are unaffected because they must contain everything they call.

Measured on backports-release-1.13 (1.13.0-rc3.32), macOS aarch64, with a cold compiled/ directory for each run:

before after 1.12.7
Base.compilecache(Preferences) 0.658s 0.253s 0.31s
Pkg.precompile(), Static 10.01s 5.11s 7.0s
Pkg.precompile(), ComponentArrays tree 13.32s 7.61s 9.5-10.2s
Preferences image size 317,824 B 256,768 B 256,800 B

The Preferences image returns to the 1.12 shape: non-specsig image callees use tojlinvoke instead of receiving a local japi1_ copy. Package load and first-call times for the ComponentArrays and ShareAdd tasks are unchanged.

This should be backported to 1.13, where the regression first appears.

…kgimage

Since JuliaLang#59361 the pkgimage worklist is compiled through
`compile_and_emit_native` -> `typeinf_ext_toplevel` -> `compile!`, which
enqueues every `:invoke` target of each compiled body and demands its
source. For callees that already live in the sysimage that source is no
longer stored (JuliaLang#58172, JuliaLang#58662), so `typeinf_ext` runs inference on them
from scratch, and `jl_emit_native` then discards the result: under external
linkage it already skips anything carrying `JL_CI_FLAGS_FROM_IMAGE`. On 1.12
the same skip existed, but the source demand was satisfied cheaply because
the sysimage still kept the IR.

The cost scales with how much Base code a package's compiled methods call.
`Base.compilecache` of Preferences goes from 0.31s on 1.12.7 to 0.65s on
1.13.0-rc3; Static from 7.0s to 11.0s. In-process, `typeinf_ext_toplevel`
on the single `Preferences.load_preference(::UUID, ::String, ::Nothing)`
entry point returns 707 CodeInstances in 0.33s, all of which already have
native code (487 in Base, 92 in Base.TOML, ...), against 483 in 0.02s on
1.12 where 481 of them still carried IR. A native sample of the Static
precompile child puts the whole 1.12 -> 1.13 difference in
`typeinf_ext_toplevel` under `jl_create_native_impl`, with LLVM time
unchanged.

Skip callees that already have native code from an image when the output
is a package image (external linkage, not trimming), which is exactly the
set `jl_emit_native` was going to discard. Codegen calls them through the
image's fptr as before. Trimmed and system images are unaffected. With the
skip, the entry point above compiles to 1 CodeInstance in negligible time.

Assisted-by: Claude Code (Fable 5)
Claude-Session: https://claude.ai/code/session_01VzpF8n8QdxxtrbpjgEgW6J
@IanButterworth IanButterworth added packages Package management and loading latency Latency backport 1.13 Change should be backported to release-1.13 labels Aug 31, 2026
@KristofferC KristofferC added this to the 1.13 milestone Aug 31, 2026
For an entry point whose callees are compiled into the sysimage,
`typeinf_ext_toplevel` with `external_linkage=true` must return only the
entry point's CodeInstance, while without it the image callees are still
collected.

Assisted-by: Claude Code (Fable 5)
Claude-Session: https://claude.ai/code/session_01VzpF8n8QdxxtrbpjgEgW6J
@IanButterworth
IanButterworth marked this pull request as ready for review August 31, 2026 13:37
@IanButterworth
IanButterworth requested a review from vtjnash August 31, 2026 13:38
@topolarity

topolarity commented Aug 31, 2026

Copy link
Copy Markdown
Member

Looks like a real issue. Excellent find @IanButterworth

this needlessly re-infers image callees that jl_emit_native will discard under external linkage because they carry JL_CI_FLAGS_FROM_IMAGE

This suggests that the extra re-inference is being thrown away, but that seems to be contradicted by the impact to the image size. Might be a difference between master and 1.13 - I see ~259.5KB for Preferences on my local master build (w/o this PR).

IIUC #60031 added the FROM_IMAGE discard, so presumably that accounts for the delta.

I think these are inference-only + discarded on master and inference + codegen + emitted as a redundant CI on 1.13

@IanButterworth

IanButterworth commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

This gives a 20% improvement in precompilation time on 1.13 and 10% improvement on nightly

Before
1.13 = #62926 1483760
nightly = ba658eb
results

With this PR ontop of 1.13 and nightly
results

IanButterworth added a commit to IanButterworth/Julia-TTFX-Snippets that referenced this pull request Aug 31, 2026
Same five-arm sweep with JuliaLang/julia#62934 applied to both dev arms
(local builds of master and of the release-1.13 backports branch, linked as
juliaup channels). Against the 2026-08-30 stock run: precompile geomean -20%
on the 1.13 arm (-10% after excluding the stock run's inflated cells) and
-13.5% on nightly; load and script execution move with the 1.12 control arm,
i.e. unchanged. Relative to 1.10, the 1.13 arm's precompile geomean flips
from +11% to -12%.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VzpF8n8QdxxtrbpjgEgW6J
IanButterworth added a commit to IanButterworth/Julia-TTFX-Snippets that referenced this pull request Aug 31, 2026
The investigation behind JuliaLang/julia#62934: 1.13 re-infers sysimage
callees while generating every pkgimage because the image driver demands
source that the sysimage no longer retains. Includes the MWEs, the evidence
trail, and the before/after numbers from the patched builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VzpF8n8QdxxtrbpjgEgW6J
@IanButterworth

Copy link
Copy Markdown
Member Author

@topolarity good point. Claude agrees. I've replaced

…so this needlessly re-infers image callees that jl_emit_native will discard under external linkage because they carry JL_CI_FLAGS_FROM_IMAGE

with

…so this needlessly re-infers image callees; master then discards the result under external linkage (JL_CI_FLAGS_FROM_IMAGE, #60031), while 1.13's older emit path additionally emits some of it as redundant copies in the image

@IanButterworth

Copy link
Copy Markdown
Member Author

Closing as #61479 was already prepared and is more complete.

@IanButterworth
IanButterworth deleted the ib/pkgimage-skip-image-callees branch August 31, 2026 18:12
IanButterworth added a commit to IanButterworth/Julia-TTFX-Snippets that referenced this pull request Sep 1, 2026
First sweep with per-repeat load/run times and the JIT objcache cleared per
task. Arms were local builds of master and the release-1.13 backports branch
with JuliaLang/julia#62934 applied, plus stock 1.12/1.11/1.10. Headline:
TTSX/TTFX run ratio is 0.30x on nightly (the objcache) against 0.95-0.97x on
1.10-1.13, and nightly's true cold TTFX run (0.112s geomean) is slower than
patched 1.13 (0.097s); the earlier "-64% script execution" on nightly was a
warm-cache TTSX with cross-snippet bleed. See ttfx-summary.txt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VzpF8n8QdxxtrbpjgEgW6J
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport 1.13 Change should be backported to release-1.13 latency Latency packages Package management and loading

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants