Precompile the macOS and Linux publishes too

ReadyToRun went onto the win-x64 publish in the previous change and took the main window from 2.6 s to
about 1.4 s on its own. The cause it answers — every launch JIT-compiling Avalonia, the XAML and EF
Core's model building — is the same on every desktop, so the condition is now any RID rather than win-*.

Verified in the Alpine SDK container that reproduces CI: osx-arm64 and linux-x64 both cross-compile,
`vpk '[osx]' bundle` builds the .app with its Info.plist, and scripts/ci-linux-rpm.sh builds the RPM
with its provides and requires checks passing. Each publish lists exactly the same files with
ReadyToRun as without, because the precompiled code lives inside the managed assemblies — so the
per-dylib signing loop in release-macos.sh and the RPM's library scan have nothing new to see. macOS
runs it under the allow-jit entitlement the JIT already needed.

The cost is size, as on Windows: osx-arm64 from 141 MB to 192 MB, linux-x64 from 130 MB to 172 MB.

Not measured: the startup gain on either platform, which the csproj comment says rather than implies,
and anything about the signed and notarized macOS build, which only release-macos.sh on a Mac produces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jaap-janandClaude Opus 5.5 committed 2026-10-23 16:09:14 +02:00
1 parent 3e607f57b9
commit 1426be8f63
1 file changed
+14 -7
@@ -91,10 +91,10 @@
<InvariantGlobalization>false</InvariantGlobalization>
<!--
Precompiled, on a Windows publish, because a cold start here is mostly the JIT. The runtime pack's
own assemblies already ship precompiled; ours and every package's — Avalonia, Skia's bindings, EF
Core, SSH.NET — ship as IL, and before this every launch compiled the toolkit, the XAML and EF's
model building method by method before anything reached the screen.
Precompiled, on every desktop publish, because a cold start here is mostly the JIT. The runtime
pack's own assemblies already ship precompiled; ours and every package's — Avalonia, Skia's
bindings, EF Core, SSH.NET — ship as IL, and before this every launch compiled the toolkit, the XAML
and EF's model building method by method before anything reached the screen.
Measured on a self-contained win-x64 publish, warm disk, median of six launches: the splash from
735 ms to 536 ms after the process starts, and the main window idle on its first screen from 2.6 s
@@ -103,12 +103,19 @@
application opened many times a day and installed once — and it is a fifth of what dropping the
native symbols below saves, so the first install is still far smaller than it once was.
The numbers above are Windows's, and only Windows has been timed. macOS and Linux take the same
publish — osx-arm64 and linux-x64 are cross-compiled on the same Linux runner, and ReadyToRun code
lives inside the managed assemblies, so neither the per-dylib signing loop in release-macos.sh nor
the RPM's library scan sees a new file: both publishes list exactly the same files with it as
without. They grow the same way Windows's does, osx-arm64 from 141 MB to 192 MB and linux-x64 from
130 MB to 172 MB. On macOS it runs under the allow-jit entitlement the JIT already needs. Whether they gain as much is unmeasured; that they gain at all is the runtime's own
bet, since its framework assemblies ship precompiled for both.
Conditioned on the RID rather than set outright, so a plain `dotnet build`, `dotnet run` and every
test project referencing this one are untouched: ReadyToRun is a publish step and needs a RID to
target. Windows only for now, because Windows is where it was measured; macOS and Linux publish the
same way and would very likely gain the same, but nothing here has timed them.
target.
-->
<PublishReadyToRun Condition="$(RuntimeIdentifier.StartsWith('win-'))">true</PublishReadyToRun>
<PublishReadyToRun Condition="'$(RuntimeIdentifier)' != ''">true</PublishReadyToRun>
</PropertyGroup>
<ItemGroup>