1# Schema construction profile, 2026-09-25
2
3The largest attributable costs depend on the schema. AST creation and copying dominate the ready-field wide structs
4and the checked/encoded fixtures. Central schema-object construction is substantial when many small schemas are composed.
5Type projection is more prominent with new transformations than with ordinary checks. These measurements do not support
6assigning one universal percentage to `Schema.make` or predicting an optimization's speedup from CPU shares.
7
8Only measurement code was added. The measured library is `main` at
9`e36c35c656bb5cc6dd1170d863ea8abb75c24279`. Environment: Apple M3, macOS Darwin 23.6.0 arm64,
10Node v24.12.0, V8 13.6.233.17-node.37. The source tree was checked clean and the revision unchanged before and after each run.
11
12## Elapsed construction time
13
14Times below are medians from 24 ordinary fresh Node processes per fixture, without the profiler.
15Each process constructs 1,000 roots. MAD is the median absolute deviation, not a confidence interval.
16Imports and first decode are excluded. Child construction is included.
17
18| Fixture | Median ms | MAD ms |
19| ------------------------------------------ | --------: | -----: |
20| Literal | 0.648 | 0.008 |
21| Struct, 2 dynamically generated fields | 1.249 | 0.018 |
22| Struct, 32 dynamically generated fields | 6.126 | 0.412 |
23| Struct, 256 dynamically generated fields | 43.040 | 0.655 |
24| Nested structs | 5.356 | 0.110 |
25| Arrays, checks, optional keys and literals | 18.455 | 0.310 |
26| New string-to-number transformations | 12.360 | 0.108 |
27| Union of 8 new tagged members | 22.030 | 0.180 |
28| Struct, 32 ready fields | 3.955 | 0.043 |
29| Struct, 256 ready fields | 26.528 | 0.197 |
30
31The dynamic-field fixtures include `Object.fromEntries` and its input arrays. The ready-field fixtures isolate
32`Schema.Struct(fields)` with a previously prepared field map. Neither dynamic generation nor reused fields is a timing
33model for a freshly written object literal. The ready-field cases were measured in a subsequent cohort; the difference
34between the two tables' cases is not a paired estimate of field-map creation cost.
35
36## Attributable internal work
37
38These are mean per-process sample percentages at a requested 100 microsecond interval, rounded to one decimal place.
39Every sample belongs to one category. Other includes API preparation, separately identifiable maker factories, the caller,
40and runtime frames. Library work that V8 folds into the caller can remain in Other.
41
42| Fixture | Initial AST and AST changes | Central schema constructor | `toType` | GC | Other |
43| ------------------------------------------ | --------------------------: | -------------------------: | -------: | ----: | ----: |
44| Struct, 256 ready fields | 49.7% | 1.7% | 9.3% | 15.8% | 23.5% |
45| Nested structs | 14.4% | 26.6% | 6.9% | 26.9% | 25.2% |
46| Arrays, checks, optional keys and literals | 33.6% | 21.4% | 3.7% | 23.1% | 18.2% |
47| New string-to-number transformations | 30.9% | 21.9% | 19.3% | 12.5% | 15.4% |
48
49Representative 95% process-bootstrap intervals:
50
51| Fixture | Initial AST and AST changes | `toType` |
52| ------------------------------------------ | --------------------------- | -------------- |
53| Struct, 256 ready fields | 48.8% to 50.6% | 8.4% to 10.2% |
54| Arrays, checks, optional keys and literals | 32.5% to 34.8% | 3.1% to 4.4% |
55| New string-to-number transformations | 28.8% to 33.0% | 17.4% to 21.1% |
56
57The intervals describe process-to-process sampling variability. They do not include profiler bias, missed boundary
58samples, or attribution errors caused by optimized code. These shares must not be multiplied by ordinary elapsed time
59and presented as directly measured phase durations.
60
61The retained stacks point to these concrete operations:
62
63- Checked schemas spend many AST samples in `modifyOwnPropertyDescriptors` and `replaceChecks` in
64 [SchemaAST.ts](../../../src/SchemaAST.ts). Updating checks copies property descriptors and creates another AST object.
65- Encoded schemas also spend substantial time in `replaceEncoding`. The stacks separate copies needed while defining
66 transformations from copies made later by `toType` while preparing `.make`.
67- Ready-field structs spend many AST samples in `struct` and the `Objects` constructor. Those enumerate fields, create
68 property signatures and check duplicate names. `toType` then traverses the property signatures, using memoized children.
69- The [central constructor](../../../src/internal/schema/make.ts) remains a substantial cost for nested, checked,
70 encoded and union fixtures. Its samples include function/prototype/property setup and inseparable inlined work.
71 They do not isolate the cost of each property assignment or closure allocation.
72- GC is visible in several fixtures. The CPU profile cannot assign a GC pause to the allocation that caused it.
73 Small or zero samples in `makeEffect`/`makeOption` therefore do not establish that their allocations are cheap.
74
75## Profiler perturbation and resolution
76
77Both frequencies were paired with ordinary execution in the same round. Entries are median paired changes in elapsed
78time, with 95% intervals. This is measurement overhead, not a source-code regression.
79
80| Fixture | Profiling at 100 us | Profiling at 500 us |
81| ------------------------------------------ | ---------------------- | ---------------------- |
82| Literal | +13.8%, 12.4% to 16.5% | +13.0%, 12.0% to 15.4% |
83| Struct, 2 dynamic fields | +15.7%, 11.9% to 18.2% | +14.6%, 12.6% to 17.6% |
84| Nested structs | +6.2%, 3.7% to 7.7% | +2.2%, 0.6% to 4.5% |
85| Arrays, checks, optional keys and literals | +6.1%, 3.8% to 7.6% | +3.2%, 1.1% to 4.7% |
86| New string-to-number transformations | +6.4%, 5.4% to 8.0% | +4.4%, 2.4% to 5.9% |
87| Struct, 256 ready fields | +5.8%, 4.0% to 7.0% | +2.9%, 2.2% to 3.3% |
88
89The checked and encoded fixtures give similar broad proportions at both frequencies. At 500 us, their `toType` shares
90are 3.9% and 19.4%, respectively. This supports distinguishing the workloads, not treating the percentages as exact costs.
91
92Literal has only 147 total construction samples at 100 us and 26 at 500 us across all 24 processes. The 500 us median
93sampled span is zero because most processes have only one observed construction sample. Struct-2 and ready-field Struct-32
94also have sparse 500 us profiles. Their precise elapsed times do not provide a comparably precise internal decomposition.
95
96Union-8 exposes a boundary limitation: the 500 us median sampled span covers about 81% of elapsed construction time.
97Its GC share changes from 34.7% at 100 us to 26.1% at 500 us, while central construction stays near 28%.
98Do not use its GC point estimate to quantify a prospective allocation optimization. Caller-only attribution is also
99ambiguous: even ready-field Struct-256 assigns about 21% to its tiny calling function, showing that this includes inlined
100library work. It must not be described as 21% benchmark overhead.
101
102## Reproduction and validation
103
104The [protocol](./README.md) describes timing boundaries, attribution and sparse-profile handling. Two complete cohorts
105ran sequentially, with no builds, tests or other benchmarks started alongside them. They contain 720 measured processes
106and 10 separate semantic validation processes. All configured rounds succeeded; no observations were discarded.
107An earlier two-round pilot and an incomplete sampler-resolution probe are separate from these results.
108
109```sh
110node packages/effect/runtimeperf/suites/schema-construction/run.mts tmp/schema-construction-rerun 24 1000 literal,struct-2,struct-32,struct-256,nested,composed,encoded,union-8
111node packages/effect/runtimeperf/suites/schema-construction/run.mts tmp/schema-construction-ready-rerun 24 1000 struct-ready-32,struct-ready-256
112```
113
114Raw reports: [main cohort](../../../../../tmp/schema-construction-main/results.json) and
115[ready-field cohort](../../../../../tmp/schema-construction-ready/results.json). Their sibling `profiles/` directories
116contain the CPU profiles. Their `harness/` directories preserve the measured analysis files. The fixture list and runner
117archival support were extended between cohorts; each report records its own hashes. Library sources and measurement
118settings were unchanged.
119
120Validation passed: all 26 runtimeperf infrastructure tests, including four attribution tests; valid/invalid fixture
121checks and type-side makers in separate workers; post-timing root checks; `pnpm lint-fix`; focused `.mts` formatting;
122and `pnpm check`. No library optimization was implemented or measured.