N/ NIFT LABS

Experiment 02 / build architecture

From source files
to 10,000 pages.

A controlled throughput experiment in four build systems. Minimal content, tool-native sources, checked routes and explicit full/incremental boundaries.

10,000 minimal pagesNift · Hugo · Astro · VitePress4 vCPUs / Nift −1 / Hugo default5 samples + 1 warmup
01 / Fresh-fixture full
BUILD START / 0120s / BUILD END
Nift
1.267s
Hugo
1.849s
Astro
116.0s
VitePress
103.9s
02 / Warm full
BUILD START / 0120s / BUILD END
Nift
0.941s
Hugo
1.754s
Astro
112.3s
VitePress
96.792s

Zero-origin 0–120s lanes · medians · shared dependencies/cache prepared. Minimal-page throughput, not a real-site ranking. State and fairness boundaries ↓

03 / Nift changed inputNo-op0.364sOne leaf0.357sShared template1.194s

Nift-only production iteration; no competitor incremental/HMR comparison. Byte equality with full recomputation is checked.

01 / prepared outside timingSource corpus

100 → 1,000 → 10,000 title/heading/body pages.

02 / timedBuild system

Fresh application state or warm full build; all four CPUs.

03 / validated afterwardsRendered output

Exact routes, titles, headings and body content.

Current Nift v4.12.0 final candidate; all tools may use the same four-vCPU host. Exact source, worker policies, retained samples and earlier cohorts are identified below. Historical measurements remain separate.

10,000-page corpus

Time and memory, with boundaries attached.

Incremental peak memory

Memory for changed-input production publications, separate from full-build memory and elapsed time. Linear bars start at zero; exact values are shown in MiB.

Kernel waited-child high-water RSS; not aggregate simultaneous process-tree RSS
020 MiB

100 pages / no-op

Nift5.8 MiB

100 pages / one-page

Nift6.9 MiB

100 pages / shared-template

Nift6.9 MiB

1,000 pages / no-op

Nift6.0 MiB

1,000 pages / one-page

Nift7.0 MiB

1,000 pages / shared-template

Nift7.8 MiB

10,000 pages / no-op

Nift10.7 MiB

10,000 pages / one-page

Nift11.7 MiB

10,000 pages / shared-template

Nift17.9 MiB

100 pages / explicit target

Nift6.9 MiB

1,000 pages / explicit target

Nift7.0 MiB

10,000 pages / explicit target

Nift11.7 MiB

Five measured samples per cell: median [min–max]. All-core same-host final v4.12; explicit targets retain a separate serial cohort. No competitor incremental/HMR comparison. Full case tables and evidence below retain the other scopes and workloads.

At 10,000 pages, Nift’s fresh-fixture full-build median was 1.267 s and Hugo’s was 1.849 s. Astro and VitePress were 116.0 s and 103.9 s. These are observations for minimal pages, with each system’s build machinery included.

10,000 pages; fresh fixture with shared dependencies/cache, 5 measured builds per cell
GeneratorFresh sWarm full sFresh RSS MiBWarm RSS MiB
Nift1.2670.94116.93816.812
Hugo1.8491.754245.0240.2
Astro116.0112.33,566.13,664.0
VitePress103.996.7922,512.02,550.6
Generated-page throughput, not equal output volume or a universal SSG ranking. At 10,000 pages, VitePress emits about 3.019 GB while Nift emits 1.318 MB of public output. See the audited work accounting below; these product-specific costs are included in the timings. Real-site migration reports answer different questions.
10,000 pages / all-core full builds. Median build time (s). Zero-based axis.
10,000 pages / all-core full builds. Zero-based scale. SVG · PNG. Exact values appear in the accompanying tables.
Logarithmic corpus scaling curves: current all-core full and Nift incremental builds on the left; explicit one-page targeted builds and their fresh full reference in a separate serial cohort on the same host on the right.
Three measured corpus sizes; both axes use logarithmic scales. Full-build fixtures retain shared dependencies/cache. All curves use the same four-vCPU host. Explicit targets use a separate serial cohort with their own full-build reference; samples are never pooled. SVG · PNG.
CORPUS SCALE

The cost of more pages

Median seconds; shared dependencies/cache prepared; three measured sizes, fresh and warm full
PagesBoundaryNiftHugoAstroVitePress
100Fresh fixture0.0170.0811.8492.727
100Warm full0.0150.0812.0232.815
1,000Fresh fixture0.1410.2376.4469.380
1,000Warm full0.1070.2576.8839.310
10,000Fresh fixture1.2671.849116.0103.9
10,000Warm full0.9411.754112.396.792

Three measured points, not a fitted complexity model. Fixture/dependency preparation is outside timing; generated output is produced inside it.

10,000-page memory footprint. Median waited-child RSS (MiB). Logarithmic axis.
10,000-page memory footprint. Logarithmic scale; bar lengths are not proportional to values. SVG · PNG. Exact values appear in the accompanying tables.
Same host: Nift incremental medians across corpus sizes; 5 measured samples
PagesNo-op sOne changed page sShared template s
1000.0080.0080.018
1,0000.0440.0460.149
10,0000.3640.3571.194
Separate same-host cohort: Nift targeted-build medians; 5 measured samples + 1 warmup
PagesFresh full reference sExplicit page-1 target s
1000.0160.006
1,0000.1290.022
10,0001.2510.163

Explicit target means nift build page-1 after changing that one leaf in an already fully built corpus. It does not scan/build all changed pages or rebuild a shared-template change. Every sample validates the complete corpus, unchanged unrelated pages, and byte equality with a full rebuild. The target cohort uses this same four-vCPU host, exact candidate binary and worker policy. Its separate fresh-full reference is not pooled with the main full-build samples.

CHANGED INPUT

Nift’s development loop

Nift only, 10,000 pages; no cross-generator incremental claim
ChangeMedian sMin–max sRSS MiB
no-op0.3640.351–0.41410.684
one-page0.3570.351–0.38111.688
shared-template1.1941.176–1.21117.934

One independent leaf or the shared template is changed before each run. Every resulting output set must match a full rebuild of that same mutated input byte-for-byte.

Iteration memory / peak RSS

Incremental memory matters alongside elapsed time: a smaller affected build can leave more headroom for concurrent builds, CI jobs and agents on constrained machines. Shared changes and retained worker pools can still use substantial memory; an incremental build is not guaranteed to have a lower peak than a full build. Actual concurrency and total workflow productivity were not benchmarked.

Kernel waited-child high-water RSS; not aggregate simultaneous process-tree RSS. Five measured samples per cell: median [min–max]. All-core same-host final v4.12; explicit targets retain a separate serial cohort. No competitor incremental/HMR comparison.

Peak RSS in MiB · accepted timings unchanged
CaseNift
100 pages / no-op5.85.6–5.9
100 pages / one-page6.96.8–6.9
100 pages / shared-template6.96.8–7.0
1,000 pages / no-op6.05.9–6.1
1,000 pages / one-page7.06.9–7.1
1,000 pages / shared-template7.87.8–7.9
10,000 pages / no-op10.710.6–10.7
10,000 pages / one-page11.711.6–11.7
10,000 pages / shared-template17.917.7–17.9
100 pages / explicit target6.96.8–7.0
1,000 pages / explicit target7.07.0–7.2
10,000 pages / explicit target11.711.6–11.8

Different scopes and hosts remain separate; full-build memory is not substituted for an edit. Raw measurements, provenance and memory scopes ↗

Equivalence before speed

The output has to contain the page.

Every build checks the complete corpus route set and each page’s exact title/heading and expected body. Hugo raw HTML is enabled; Astro uses file routes; VitePress has a minimal custom theme, retains hydration assets and generates one extra validated 404 page.

Fresh-fixture builds (raw mode application-cold) recreate the entire project fixture; shared pinned node_modules/toolchains and their generated dependency cache remain prepared across the series. Astro/Vite’s shared node_modules/.vite cache is inventoried. This does not measure a complete reset of all application caches. Warm full removes generated output/cache directories while retaining other application build state. Both are full builds; neither is a machine-cold boot. Nift build_threads=-1; Hugo GOMAXPROCS is unset; every timed process can use CPUs 0–3. Full builds invoke nift build --all; normal incremental builds invoke nift build. These command boundaries are distinct.

One warmup plus five measured samples per cell, rotating tool order. C-supervised fork/exec-to-exit latency excludes Python/supervisor launch. Kernel waited-child RSS is a high-water metric, not simultaneous aggregate tree memory; concurrent children/shared pages require a separate accounting boundary.

Old results are incomparable. Corrected Hugo content, minimal VitePress theme, route parity, application state and direct timing change the old workload. Schema-4 used sampled aggregate RSS with a 10 ms polling loop. No historical speedup is claimed.
Exact warm-full cleanup; internal state is tool-specific
GeneratorRemoved before warm fullRetained state
Niftpublic/.nift/ tracking metadata
Hugopublic/, resources/other project state
Astrodist/, .astro/shared node_modules dependency cache
VitePressdocs/.vitepress/dist/, docs/.vitepress/cache/shared node_modules dependency cache

Observation → interpretation

A small build surface is useful evidence.

Different preparation, rendering, bundling and hydration paths appear in these costs, while their features are mostly unused by minimal pages. Nift also has a broader scripting/build role than a conventional SSG. The fixture does not exercise complex Markdown/MDX, assets, component graphs or maintained production websites.

Warm full names the retained-state workflow, not an expected speedup. Different totals do not identify individual planning/rendering/cache phases; those were not profiled. Scaling describes this fixture. Competitor incremental server/HMR behaviour was not measured; comparing it to Nift’s one-shot dependency build requires a new equivalent experiment.

Work performed

Matching page semantics, different emitted work.

Every tool satisfies the same route/title/heading/body contract. That does not mean equal emitted bytes or identical product features. These are separate untimed fresh builds on the same host; their counts explain the workload boundary, not a causal allocation of elapsed time.

Audited file/byte accounting; linked external dependencies are excluded from input bytes
PagesToolInput filesInput bytesOutput filesOutput bytesNew internal filesNew internal bytes
100Nift10313,47410012,78010164,941
100Hugo10210,07810012,88010
100Astro10112,90310012,78025,553
100VitePress1024,325306663,49100
1,000Nift1,003134,9741,000129,7801,001653,541
1,000Hugo1,002101,8781,000130,78010
1,000Astro1,001129,9031,000129,78025,553
1,000VitePress1,00242,1253,00631,942,09100
10,000Nift10,0031,376,97410,0001,317,78010,0016,584,541
10,000Hugo10,0021,046,87810,0001,327,78010
10,000Astro10,0011,317,90310,0001,317,78025,553
10,000VitePress10,002429,12530,0063,018,844,09100

At 10,000 pages, VitePress emits 30,006 files totalling 3,018,844,091 bytes, including about 3.007 GB across its 10,001 HTML files. Nift emits 10,000 public files totalling 1,317,780 bytes and creates 10,001 internal metadata files totalling 6,584,541 bytes; Hugo emits 1,327,780 public bytes. The manifest records these substantial output-volume and bookkeeping differences. Timing ratios describe this frozen fixture, not equal byte throughput or a universal platform ranking.

Output-type breakdown · All twelve workload inventories. Full path/size/hash manifests are retained alongside them.

Frozen environment

The same four-vCPU machine for every tool.

One fresh g6-standard-4 Linode in us-east: 4 vCPUs, 8 GiB RAM, Ubuntu 24.04; AMD EPYC 7642 48-Core Processor. All timed processes can use CPUs 0–3. Nift build_threads=-1; Hugo GOMAXPROCS is unset. Astro and VitePress retain their normal pinned benchmark configuration. This is whole-machine parallel throughput, not the earlier CPU0 cohort.

Nift v4.12.0, exact clean source c14d06835bca515a5281e41da0e40219c2d60ef0, GCC 13.3 and normal native Make -O2. CLI SHA-256 7dd42dcee5ca4a6965f0823c0ae59198282da1eb77e0fcd4cae93a537870a6fb. The unchanged suite is frozen at a2890bc504cf7c55b77cd4fddae17403b7caf8ae; the separately recorded adapter changes worker policy only.

Pinned tools on the new four-vCPU measurement host
ToolRecorded version
NiftNift v4.12.0
Hugohugo v0.167.0-3fff6fb5c267dacb26280c78dbe8c344054249c8 linux/amd64 BuildDate=2026-09-28T14:50:38Z VendorInfo=gohugoio
Node.jsv24.21.0
Astro7.3.6
VitePress1.6.4

All 39 jobs passed independent checks: 195 measured samples and 39 warmups, 234 observations. Fresh/warm full, normal incremental and explicit targets retain their own state boundaries. Their raw samples are not pooled with previous campaigns.

Exact source and binary identities · Frozen all-core sample plan · Exact commands and cleanup boundaries · Independent acceptance gate.

Reproduce / scrutinise

Every new observation is retained.

100-page raw samples · 1,000-page raw samples · 10,000-page raw samples · Separate targeted cohort · Fixture/output work accounting. Exact source, warmups, failed preflight attempts, host provenance and teardown receipts remain in the campaign. Historical evidence is unchanged.

Practitioner judgement / AI-DX

Choose the workflow as well as the build time.

For a human-authored existing Hugo/theme estate, I would stay with Hugo when its conventions already fit. For a new human-plus-agent maintained site, an agent-primary site or a mixed procedural publishing project, I would choose Nift when its APIs and explicit tracking fit the requirements. These are qualitative choices, not measured authoring success rates or a universal platform ranking.

Explicit source/output ownership, template dependencies, named targets and .f helper policy can make bounded agent edits easier to inspect. Familiarity to humans and safe agent reasoning are different questions. The measured incremental and targeted paths support practical iteration; the frozen harness checked complete routes, intended contents and full-rebuild parity. Those harness checks are not general product-native guarantees, and equivalent Hugo incremental/HMR recovery was not measured.

Conclusion

Defensible as minimal-page throughput.

The corrected fixture and output gates support this bounded experiment. Its measurement boundaries should travel with every quoted number; read real-site migration reports for architecture and maintenance tradeoffs.

Nift Benchmarks

Three perspectives on one runtime.