@@ -4,11 +4,11 @@ Related issue: [#3282](https://github.com/shakacode/react_on_rails/issues/3282)
44
55## Verdict
66
7- | Area | Status | Finding |
8- | ------| --------| ---------|
9- | vm.Script caching benefit | ** Not needed** | Execution time dominates; compile overhead is negligible for real workloads |
10- | Hypothesis from #3282 | Refuted | Expected ~ 3ms savings not achievable — real compile cost is <15μs even for 500KB code |
11- | Implementation effort | Not justified | Adds complexity for <0.01% improvement on actual render paths |
7+ | Area | Status | Finding |
8+ | ------------------------- | -------------- | ------------------------------------------------------------------------------------- |
9+ | vm.Script caching benefit | ** Not needed** | Execution time dominates; compile overhead is negligible for real workloads |
10+ | Hypothesis from #3282 | Refuted | Expected ~ 3ms savings not achievable — real compile cost is <15μs even for 500KB code |
11+ | Implementation effort | Not justified | Adds complexity for <0.01% improvement on actual render paths |
1212
1313## Executive Summary
1414
@@ -19,6 +19,7 @@ Issue #3282 proposed caching pre-compiled `vm.Script` objects to save the ~3ms "
19193 . ** The 3ms observed** was likely execution time, not parse time
2020
2121Caching ` vm.Script ` is only beneficial for scripts that:
22+
2223- Execute in <1μs (trivial expressions)
2324- Run 100+ times with identical code
2425- Have minimal actual computation
@@ -36,26 +37,26 @@ The render template in react_on_rails_pro does real work (React rendering, props
3637
3738### 1. Compilation Cost vs Code Size
3839
39- | Code Size | Compile Time | Cached Exec | Uncached Exec | Speedup |
40- | ----------- | -------------- | ------------- | --------------- | ---------|
41- | 60 chars | 0.54μs | 0.62μs | 1.37μs | ** 2.2x** |
42- | 8 KB | 0.54μs | 38.04μs | 38.54μs | ** 1.01x** |
43- | 69 KB | 2.75μs | 400.12μs | 404.00μs | ** 1.01x** |
44- | 650 KB | 0.83μs | 2016.46μs | 2016.25μs | ** 1.00x** |
40+ | Code Size | Compile Time | Cached Exec | Uncached Exec | Speedup |
41+ | --------- | ------------ | ----------- | ------------- | --------- |
42+ | 60 chars | 0.54μs | 0.62μs | 1.37μs | ** 2.2x** |
43+ | 8 KB | 0.54μs | 38.04μs | 38.54μs | ** 1.01x** |
44+ | 69 KB | 2.75μs | 400.12μs | 404.00μs | ** 1.01x** |
45+ | 650 KB | 0.83μs | 2016.46μs | 2016.25μs | ** 1.00x** |
4546
4647** Observation** : For scripts with real execution time (38μs+), caching provides <1% improvement because compilation (0.5-3μs) is noise compared to execution.
4748
4849### 2. Parse Complexity Analysis
4950
5051Different AST patterns were tested to find maximum parse cost:
5152
52- | Pattern | Code Size | Compile Time | Size Needed for 1ms Compile |
53- | ---------| -----------| --------------| ----------------------------|
54- | Nested objects | 2.7 KB | 1.0μs | ** 2.6 MB** |
55- | Generators | 9 KB | 1.2μs | 7.3 MB |
56- | Class definitions | 16 KB | 1.4μs | 11.2 MB |
57- | Regex literals | 29 KB | 1.8μs | 15.4 MB |
58- | String literals | 111 KB | 3.8μs | 27.6 MB |
53+ | Pattern | Code Size | Compile Time | Size Needed for 1ms Compile |
54+ | ----------------- | --------- | ------------ | --------------------------- |
55+ | Nested objects | 2.7 KB | 1.0μs | ** 2.6 MB** |
56+ | Generators | 9 KB | 1.2μs | 7.3 MB |
57+ | Class definitions | 16 KB | 1.4μs | 11.2 MB |
58+ | Regex literals | 29 KB | 1.8μs | 15.4 MB |
59+ | String literals | 111 KB | 3.8μs | 27.6 MB |
5960
6061** Observation** : Even the most expensive parsing pattern (deeply nested objects) requires 2.6 MB of code to reach 1ms compile time. This is unrealistic for render templates.
6162
@@ -64,33 +65,35 @@ Different AST patterns were tested to find maximum parse cost:
6465How much time is saved across N executions of a trivial script (32 chars)?
6566
6667| Executions | Cached Total | Uncached Total | Time Saved |
67- | ------------ | -------------- | ---------------- | ------------ |
68- | 10 | 15μs | 33μs | 18μs |
69- | 100 | 112μs | 183μs | 71μs |
70- | 1,000 | 627μs | 1,410μs | 784μs |
71- | 10,000 | 3,844μs | 11,043μs | ** 7.2ms** |
68+ | ---------- | ------------ | -------------- | ---------- |
69+ | 10 | 15μs | 33μs | 18μs |
70+ | 100 | 112μs | 183μs | 71μs |
71+ | 1,000 | 627μs | 1,410μs | 784μs |
72+ | 10,000 | 3,844μs | 11,043μs | ** 7.2ms** |
7273
7374** Observation** : Need ~ 10,000 executions of trivial scripts to save 7ms total. The render template runs once per request — no amortization possible.
7475
7576### 4. Heavy Computation Scripts
7677
7778Scripts that do real work (object creation, loops, array operations):
7879
79- | Script Type | Cached/exec | Uncached/exec | Speedup |
80- | -------------| -------------| ---------------| ---------|
81- | Trivial (return 1+1) | 0.37μs | 1.05μs | 2.8x |
82- | Light (loop 10x) | 0.47μs | 1.17μs | 2.5x |
83- | Medium (100 sqrt calls) | 12.87μs | 13.80μs | ** 1.07x** |
84- | Heavy (1000 objects) | 39.15μs | 39.99μs | ** 1.02x** |
80+ | Script Type | Cached/exec | Uncached/exec | Speedup |
81+ | ----------------------- | ----------- | ------------- | --------- |
82+ | Trivial (return 1+1) | 0.37μs | 1.05μs | 2.8x |
83+ | Light (loop 10x) | 0.47μs | 1.17μs | 2.5x |
84+ | Medium (100 sqrt calls) | 12.87μs | 13.80μs | ** 1.07x** |
85+ | Heavy (1000 objects) | 39.15μs | 39.99μs | ** 1.02x** |
8586
8687** Observation** : The moment execution time exceeds ~ 5μs, caching benefit drops to noise levels.
8788
8889## Why the Original 3ms Estimate Was Wrong
8990
9091Issue #3282 stated:
92+
9193> Per-request JS-parse cost: ~ 3 ms
9294
9395This measurement likely included:
96+
94971 . ** React component evaluation** (function execution, not parsing)
95982 . ** Props serialization** (JSON stringify/parse overhead)
96993 . ** Context creation** (` vm.createContext ` cost, not ` vm.Script ` compile)
@@ -101,18 +104,20 @@ Our isolated benchmark shows V8 parses 1MB of JavaScript in ~26μs. A 200-byte t
101104## Scripts That Would Benefit (Not Our Use Case)
102105
103106Caching helps when:
107+
104108``` js
105109// Config literals — 2-3x faster
106110const config = new vm.Script (` ({ apiUrl: "...", timeout: 5000 })` );
107111
108- // Simple expressions — 2-3x faster
112+ // Simple expressions — 2-3x faster
109113const formula = new vm.Script (` price * quantity * (1 - discount)` );
110114
111115// Feature flags — 2x faster
112116const check = new vm.Script (` user.role === "admin"` );
113117```
114118
115119Caching does NOT help when:
120+
116121``` js
117122// Data transformation — 1.05x (no real gain)
118123const transform = new vm.Script (`
@@ -127,13 +132,13 @@ const render = new vm.Script(`
127132
128133## Decision Framework
129134
130- | Condition | Cache vm.Script? |
131- | -----------| ------------------|
132- | Script execution < 1μs | Yes |
133- | Script execution > 10μs | No |
134- | Script runs 1x per request | No |
135- | Script is pure config/expression | Yes |
136- | Script does React rendering | ** No** |
135+ | Condition | Cache vm.Script? |
136+ | -------------------------------- | ---------------- |
137+ | Script execution < 1μs | Yes |
138+ | Script execution > 10μs | No |
139+ | Script runs 1x per request | No |
140+ | Script is pure config/expression | Yes |
141+ | Script does React rendering | ** No** |
137142
138143## Recommendation
139144
@@ -147,13 +152,15 @@ The optimization targets the wrong layer. V8's JIT compiler is not the bottlenec
1471524 . Reducing React reconciliation work
148153
149154Adding ` vm.Script ` caching would:
155+
150156- Add code complexity (cache invalidation, memory management)
151157- Provide <0.01% improvement on real workloads
152158- Create false confidence that "we optimized the VM layer"
153159
154160## Artifacts
155161
156162Benchmark scripts and raw data available at:
163+
157164- Original experiment: ` vm-script-cache/tmp/experiments/20260607T083551Z-vm-script-cache/ `
158165
159166### Raw Benchmark Output (Summary)
0 commit comments