From 93531f5864791a6b4344a7dc781d53033d9951da Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Sun, 7 Jun 2026 20:38:14 +0300 Subject: [PATCH] Add version-gated inline cache for global variable bindings Top-level identifier reads and writes previously paid a property dictionary lookup (writes: chain resolution plus a second lookup) on every access. JintIdentifierExpression now caches the resolved global descriptor when the current lexical env IS the global env and the binding is a plain writable MutableBinding data property on the real GlobalObject with no lexical declaration shadowing it. Validity is version-gated: the global object''s own-property shape version plus a new GlobalEnvironment._lexicalMutations counter (bumped when the set of global let/const declarations changes); in-place value writes bump neither. Cache hits read/write the descriptor value directly, mirroring GlobalObject.SetFromMutableBinding''s fast path. Inlining shape matters: a null pre-check on the cache field keeps the cost for non-global identifiers to a single field test, while the validator and the write arm stay out-of-line (NoInlining) - inlining them taxed local-variable loops ~7% via code growth. Gating (new GlobalAccessBenchmarks, tight same-window A/B): - GlobalVarLoop 58.40ms -> 43.26ms (-25.9%), now faster than the fixed-slot LocalVarLoop ceiling - LocalVarLoop guard clean (53.20 -> 50.89ms) - EngineComparison stopwatch wall-clock 207.5 -> 186.0 ms/iter (-10.4%) - Jint.Tests 0 failures (net10.0 + net472), PublicInterface green, Test262 99208 passed / 0 failed Co-Authored-By: Claude Opus 4.8 (1M context) --- Jint.Benchmark/GlobalAccessBenchmarks.cs | 55 +++++++++++++ .../Runtime/Environments/GlobalEnvironment.cs | 9 ++- .../Expressions/JintAssignmentExpression.cs | 79 ++++++++++++++++++ .../Expressions/JintIdentifierExpression.cs | 80 +++++++++++++++++++ 4 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 Jint.Benchmark/GlobalAccessBenchmarks.cs diff --git a/Jint.Benchmark/GlobalAccessBenchmarks.cs b/Jint.Benchmark/GlobalAccessBenchmarks.cs new file mode 100644 index 000000000..5d17d3b1b --- /dev/null +++ b/Jint.Benchmark/GlobalAccessBenchmarks.cs @@ -0,0 +1,55 @@ +using BenchmarkDotNet.Attributes; +using Jint.Native; + +namespace Jint.Benchmark; + +/// +/// Isolates top-level (global binding) variable access — the stopwatch.js shape where loop +/// counters and state live as global-object properties and every read/write pays a property +/// dictionary lookup. LocalVarLoop is the fixed-slot ceiling/guard for the same operations. +/// +[MemoryDiagnoser] +[HideColumns("Error", "Gen0", "Gen1", "Gen2")] +public class GlobalAccessBenchmarks +{ + private Engine _engine = null!; + private Prepared