Repository navigation
Benchmark @angular/build:library against ng-packagr and incremental builds - #1
Merged
Merged
Conversation
Upgrade the Angular CLI packages to 22.3.0-next.1, which ships the new Oxc/Rolldown-based @angular/build:library builder, and use it for the `build` target of every app0 lib. Entry points now come from the package.json `exports` field. Keep ng-packagr around as a `build-ng-packagr` target (plus a matching target on app0) writing to dist/ng-packagr, so both builders can be benchmarked side by side. The shell lib's production tsconfig points its lib imports at dist, which @nx/angular:package used to do automatically. Without it the shell type-checked every other lib from source (37s instead of 1s). @angular/build 22.2+ no longer exposes SourceFileCache as a Map, which breaks the Analog Vitest plugin, so the Analog packages and the app's Vitest setup are removed.
tools/generate-entry-point-libs.mjs generates 6 apps, each with 6 feature libs, plus 3 shared UI libs that every app uses. Every lib is split into 51 secondary entry points with 8 components each, and every component has an inline SCSS stylesheet. Feature libs consume the shared libs from dist in their production build. Each lib has both a `build` (@angular/build:library) and a `build-ng-packagr` (@nx/angular:package) target, so the two builders can be compared on the same code. The benchmark now runs per demo (app0 and entry-points), builds all of a demo's apps with run-many, adds a "shared lib changed" scenario, clears ng-packagr's own cache for cold runs and reports lib and app task time separately. The Benchmark workflow takes the demos to run and allows up to 6 hours.
Every mode runs with the same --parallel value (Nx's default of 3 unless set). The Benchmark workflow defaults to 2, because building three entry-points apps from source at once peaks around 17 GB, more than the 16 GB GitHub runner has.
--styles=css writes the component stylesheets as plain CSS instead of SCSS, and --layout=single puts every folder of a lib behind a single primary entry point instead of one secondary entry point each. Together they make it possible to compare the builders on otherwise identical code. --demo=<name> generates a variant next to the default demo. The defaults now match the committed demo, and its output is unchanged.
The Benchmark workflow now fans out into a job per demo and mode, so the slow combinations run in parallel on separate runners, and a report job merges their results.json files with the new `benchmark.mjs --report=<dir>` mode. It defaults to a single iteration and can also run the memory and builder-factor experiments as their own jobs.
ng-packagr only accepts sources below the entry point's own folder, so with --layout=single every folder now lives in <lib>/src/<folder> instead of next to src. The default layout is unchanged.
tools/memory-experiments.mjs measures how much memory building the entry-points apps needs from source vs incrementally: with Angular's NG_BUILD_PARALLEL_TS/NG_BUILD_MAX_WORKERS knobs, under shrinking --max-old-space-size limits, and with different Nx parallelism. tools/builder-factors.mjs builds the same shared lib with both builders four ways (secondary entry points or a single entry point, SCSS or plain CSS) to isolate what makes ng-packagr slower.
SCSS makes ng-packagr much slower once a lib has many secondary entry points (it sets up its Sass workers again for every entry point), while @angular/build:library barely notices it. The entry-points-css demo is the same 6 apps and 39 libs generated with --styles=css, so the benchmark can show the difference end to end. Each generated demo is selected by its own demo:<name> tag.
With no --max-old-space-size, building app0 from source runs out of memory in the Angular compiler worker, while both incremental builds of app0 succeed.
Rewrite the README around the three demos: what is measured and how, build time and memory results locally and on CI, the out-of-memory limits of building from source, the effect of Angular's thread settings and Nx parallelism, what makes @angular/build:library faster than ng-packagr, and when each approach is better or worse.
Every app's build already depends on ^build, so with buildLibsFromSource: true a plain `nx build` (and CI's run-many) built every lib and then compiled all of them again inside each app. Apps now consume dist by default; the benchmark's from-source mode and the memory experiments ask for buildLibsFromSource=true explicitly.
The single CI memory job lost its runner after 67 minutes, most likely while building six apps from source three at a time on 16 GB, and recorded nothing because results were only written at the end. The script now saves after every build, and the workflow runs each memory experiment as its own job. Document the failure and the default-heap numbers in the README.
An empty demos input skips the benchmark and report jobs instead of failing on an empty matrix.
Test three heap limits instead of four, drop the slow --parallel=1 from-source run, and move the from-source build with --parallel=3, which exhausts a 16 GB runner, into its own memory-parallel-limit job. Experiment jobs now time out after 30 minutes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Switches the workspace to the new Rolldown/Oxc-based
@angular/build:librarybuilder (Angular CLI 22.3.0-next.1) and turns the repo into a set of benchmarks comparing it with ng-packagr, and incremental builds with building from source, in build time and memory.What's in here
buildtarget uses@angular/build:library(entry points frompackage.jsonexports). ng-packagr stays available as abuild-ng-packagrtarget writing todist/ng-packagr. Apps now default tobuildLibsFromSource: false, sincedependsOn: ["^build"]builds the libs anyway.tools/generate-entry-point-libs.mjs): 6 apps with 36 feature libs + 3 shared UI libs, each split into 51 secondary entry points (17,628 components), once with SCSS (entry-points) and once with plain CSS (entry-points-css).tools/benchmark.mjs): cold / one app lib changed / one shared lib changed / cache hit, for from source vs incremental with each builder, recording wall time, peak RSS and lib vs app task time. The CI workflow runs one job per demo and mode and merges the results.tools/memory-experiments.mjs(default heap, heap limits, Angular thread settings, Nx parallelism) andtools/builder-factors.mjs(entry points × SCSS/CSS on one lib).Headline results
app0runs out of memory when built from source with Node's default heap, while both incremental builds succeed.NG_BUILD_PARALLEL_TS=0/NG_BUILD_MAX_WORKERS=1don't change memory meaningfully.Notes
@angular/build22.2+ breaks the Analog Vitest plugin, so Analog and the apps' unit tests are removed.npm ci --legacy-peer-depsbecause of the CLI prerelease..repos/folder.