next.js
3e024743 - [turbopack] Optimize `export const` to bind readonly values not getters (#82760)

Commit
252 days ago
[turbopack] Optimize `export const` to bind readonly values not getters (#82760) ## Optimize ESM Exports ### What This builds on the work from #82214 For an esm module like ``` export const foo = 2; ``` turbopack will generate a factory function like ``` __turbopack_context__ => { __turbopack_context__.s(['foo', ()=>foo]); const foo = 2; } ``` - we expose the exports early to enable circular imports between modules - we expose as 'getter functions' to enable live bindings These behaviors are necessary to uphold the ESM specification, however, we can optimize it by avoiding getter functions most of the time ``` const foo = 2; __turbopack_context__ => { __turbopack_context__.s(['foo', 0, foo]); } ``` If we can statically detect that the module is not part of an import cycle then we can delay registering its exports until the end of evaluation. Then if we also determine that a given export is never mutated (after module evaluation) then we can export it as a value instead of a getter. We use a small numeric discriminator flag this to the runtime (otherwise exporting a function is ambiguous). This approach will save code size (2 bytes per const binding), memory (no functions to allocate), and improve parsing and execution performance. Currently we use `export const` as a hint and `export default <expr>` to decide that a binding is not 'live'. Future PRs will enhance this to cover more cases. # Performance the module-cost benchmark just uses `export const` so that is enough to prove the effectiveness of avoiding getters. ## Benchmark Results: CHANGE vs HEAD (Median Values) | Scenario | Metric | HEAD (ms) | CHANGE (ms) | Speedup | % Improvement | | --- | --- | --- | --- | --- | --- | | **Client CommonJS** | Load Duration | 19\.80 | 19\.60 | 1\.01x | \+1.0% ✅ | | | Execute Duration | 19\.30 | 19\.00 | 1\.02x | \+1.6% ✅ | | **Client ESM** | Load Duration | 18\.15 | 17\.70 | 1\.03x | \+2.5% ✅ | | | Execute Duration | 46\.85 | 27\.95 | 1\.68x | \+40.3% ✅ | | **Pages API CommonJS** | Load Duration | 19\.41 | 19\.52 | 0\.99x | \-0.6% ❌ | | | Execute Duration | 25\.34 | 25\.15 | 1\.01x | \+0.7% ✅ | | **Pages API ESM** | Load Duration | 26\.02 | 22\.08 | 1\.18x | \+15.1% ✅ | | | Execute Duration | 56\.30 | 35\.12 | 1\.60x | \+37.6% ✅ | As expected this is a substantial improvement to ESM. The CJS results are just noise and should provide an idea of the variance of the run. Load time improves due to smaller and simpler code Execution time improves since there are fewer function objects to construct
Author
Parents
Loading