Compiled before it ships

Pinch or Ctrl+scroll to zoom; the text is selectable. Source · TheYard

Compiled before it ships: the same container compiled by the runtime on its one shared core, and compiled ahead of time at the build Compiled before it ships: the same container, compiled by the runtime and compiled ahead of time Every box is a step; the path under each title is where that step lives. The plan has one core, shared by both sites, and every new container starts on it. Compiled by the runtime: before 1.0.3.94 The image carries IL. Every method is compiled on the one core the first time it is called. Compiled ahead of time: 1.0.3.94 on The image carries machine code too. The runtime compiles again only what turns out hot. The build dotnet publish -c Release The C# compiler writes intermediate language, IL: portable, and not yet machine code. The image carries the IL for this app and its packages, 221.1 MB in the registry with native files for every platform the packages know. IL only The container starts App Service, one instance, one core shared by both sites The runtime loads the assemblies and finds IL. Nothing in them can run until it is compiled, and the only compiler on the machine is the one inside the running process. load, then compile Every method, compiled at its first call the JIT, on the plan's one core Each method is turned into machine code the first time it is called, on the same core that is checking the schema, loading a hundred thousand vehicles and answering the first visitors. 32,690 methods in the first two minutes on 1.0.3.93, and 113 seconds of compiling counted across every thread. run, count, recompile The hot methods, compiled again tiered compilation with PGO The runtime counts how often each method runs and compiles the hot ones a second and a third time at full optimization. 62,432 methods by the thirteenth minute, 66,685 by the seventy-fifth. answer The first visitors /readyz, then the listing Ready 79 seconds after the process started on 1.0.3.93, most of it waits on the network that compiling cannot shorten; the compiling shared the core with those waits and with the first answers. The build Dockerfile, region api-publish Restore for linux-x64 with ReadyToRun asked for, which fetches the ahead-of-time compiler. Publish compiles this app and its packages to machine code for that platform and keeps the IL beside it. One switch; nothing in the code changes. 206.2 MB in the registry, 15 MB smaller, because one platform needs one set of native files. IL and machine code The container starts App Service, the same plan The runtime loads the assemblies and finds machine code already there for this app, EF Core, the Cosmos DB SDK and Identity. The framework in the aspnet base image shipped this way already. load, then run Runs as compiled ReadyToRun code, the first tier A method that arrived compiled runs without a visit to the JIT. The runtime compiled 12,273 to 16,525 methods itself at the first read across four rolls, 49 to 62 per cent fewer than before. count, recompile the hot ones The hot methods, compiled again TieredCompilation and TieredPGO, api/TheYard.Api/TheYard.Api.csproj Still on. Compiled-ahead code is the first tier; the methods that turn out hot are compiled again with instruments and then at full optimization, so a warmed process gives up nothing. That second compile is why the time spent compiling did not fall with the count. answer, and report The first visitors /api/admin/metrics, region runtime-metrics The reading that says what happened: methods the runtime compiled itself and its time compiling, beside the collector's mode. Ready read 71 to 161 seconds across the four rolls, on a core the plan's meter showed at 85 to 99 per cent from before the containers began. The dashed line is the work that moved: from the container's first minutes to the build. What held across four rolls: 49 to 62 per cent fewer methods compiled by the runtime at the first read, every time. What did not: no faster start measured, on a core the meter showed saturated through every roll. What would settle it: the same load in Docker on one machine with the plan's limits and nothing else on the core, compiled ahead of time and not. Source: docs/images/compiled-ahead.svg in the repository, drawn by docs/images/compiled-ahead.mjs and redrawn when a step moves.