Skip to content

Commit 9e5efe8

Browse files
committed
ADR 0007: the fuller +1.5 KB protocol cost (atom + collection waves)
1 parent 96db769 commit 9e5efe8

1 file changed

Lines changed: 39 additions & 25 deletions

File tree

doc/ai/adr/0007-deferred-core-fn-costs.md

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,38 @@ defer, with the trigger that should reopen each.
1111

1212
### Protocol dispatch bundle cost
1313

14-
The protocol layer (0.14.203) made the common core fns reference their protocol
15-
slot to dispatch to custom types: `assoc` names `IAssociative__assoc`, `conj`
14+
Protocol dispatch arrived in two waves, both pinning machinery into plain-data
15+
bundles.
16+
17+
Wave 1, atoms (0.14.201): `IReset`/`ISwap`/`IWatchable`/`IAtom`/`IDeref` were
18+
added to `atom`/`swap!`/`reset!`/`deref`/`add-watch`. An app using plain atoms
19+
(`swap!`, `add-watch`, no custom `IAtom` type) pins the lot.
20+
21+
Wave 2, collections (0.14.203-205): `assoc` names `IAssociative__assoc`, `conj`
1622
names `ICollection__conj`, `get` names `ILookup__lookup`, `=` names
17-
`IEquiv__equiv`, and so on. ADR 0006 and `symbol-pure-dce` shake out the slots an
18-
app does not use. But a broad app uses `assoc` and `conj` and `count` and `get`
19-
and `=` together, so the union of its fns references the whole slot set, plus
20-
`INSTANCE_TYPE`. That block is then pinned into the bundle.
23+
`IEquiv__equiv`, plus `INSTANCE_TYPE`. A broad app uses these together and pins
24+
the whole set. ADR 0006 and `symbol-pure-dce` shake out slots an app does not
25+
use; the union a real app references survives.
2126

22-
Measured on the js-framework-benchmark reagami app (same compiled app, only the
23-
runtime core.js swapped):
27+
Measured with the js-framework-benchmark reagami app, a clean official
28+
`build-prod` per squint version (compiled and bundled entirely on each version,
29+
gzipped):
2430

25-
| runtime | raw (min) | gzip |
31+
| squint | gzip | note |
2632
|---|---|---|
27-
| main | 25973 | 9197 |
28-
| 0.14.202 (pre-protocol) | 24136 | 8659 |
29-
| delta | +1837 | **+538** |
30-
31-
reagami and the demo define no `deftype`/`defrecord`/`extend-type`, so for them
32-
the whole block is dead weight: it dispatches to custom impls that do not exist.
33-
This is true of most plain-data apps.
33+
| 0.14.197 | 7517 | before both waves |
34+
| 0.14.200 | 7705 | before atoms |
35+
| 0.14.201 | 8644 | +939, atom protocols |
36+
| 0.14.202 | 8648 | before collections |
37+
| 0.14.205 | 9520 | collection protocols, unfixed |
38+
| 0.14.206 | 9190 | collections, after symbol-pure-dce + plain-data (-330 vs 205) |
39+
40+
Net, about 1.5 KB of the 9.0 KB shipping app (~17%) is protocol dispatch
41+
machinery: ~+939 atoms plus ~+542 collections. reagami and the demo define no
42+
`deftype`/`defrecord`/`extend-type`, so all of it is dead weight - it dispatches
43+
to custom impls that do not exist. (An earlier note swapped only the runtime
44+
core.js and reported +538; that captured the collection wave alone, undercounting
45+
the total by ~3x.)
3446

3547
The fix is to invert the dispatch, like CLJS: base fns handle only native types
3648
(Object/Array/primitive/Map/Set) and name no protocol slot; `extend-type` and
@@ -59,10 +71,11 @@ doc/ai/ideas.md "Emit direct fixed-arity calls" idea.
5971

6072
Defer both. Ship neither fix now.
6173

62-
- The protocol bundle cost is ~1/2 KB gzip per broad plain-data app. Recovering
63-
it means reworking the protocol system that landed weeks ago, with regression
64-
risk to records, transients and `extend-type`. The trade does not clear the bar
65-
today.
74+
- The protocol bundle cost is ~1.5 KB gzip (~17% of the shipping app), in two
75+
waves. At that size the win clearly justifies the work; what defers it is
76+
risk, not payoff. Recovering it means reworking the protocol system that
77+
landed weeks ago, with regression risk to records, transients and
78+
`extend-type`. Do it once that code has settled, not against fresh code.
6679
- The variadic runtime patch was tried in #961 and dropped: a 2-arg `min`/`max`
6780
arity that node cannot show a win for and that Chrome does not benefit from. The
6881
real fix is the compiler-level per-arity emission, which is larger.
@@ -72,15 +85,16 @@ Defer both. Ship neither fix now.
7285
- `bb test:size` runs in CI (this ADR's companion change), so the protocol cost
7386
is visible per commit and a regression past the threshold fails the build.
7487
- reagami sidesteps the `min` cost with `js/Math.min` and does not depend on
75-
either fix. It stays current with squint and pays the ~1/2 KB, still the
88+
either fix. It stays current with squint and pays the ~1.5 KB, still the
7689
smallest app in the benchmark table.
7790
- Both fixes remain open, documented with measurements, ready to execute.
7891

7992
## When to revisit
8093

81-
- Protocol bundle cost: when `test:size` shows the tax crossing a threshold that
82-
matters, or a size-sensitive consumer is blocked by it - by then the protocol
83-
code is battle-tested and the inversion is far less risky than doing it now
84-
against fresh code.
94+
- Protocol bundle cost: the ~17% is already a size that matters, so the gate is
95+
not the payoff but the protocol code settling. Revisit once records,
96+
transients and `extend-type` have proven stable in the wild, or sooner if a
97+
size-sensitive consumer is blocked. `test:size` keeps the number honest
98+
meanwhile.
8599
- Variadic call cost: alongside the fixed-arity codegen work in doc/ai/ideas.md,
86100
which supersedes the reverted runtime patch.

0 commit comments

Comments
 (0)