Optimize Date operations and object resolution#2288
Merged
Conversation
…watch benchmark improvement Three targeted optimizations for Date-heavy workloads: 1. TypeConverter.ToNumeric: Fast path for JsDate objects Bypasses the expensive ToPrimitive chain (Symbol.toPrimitive lookup -> exotic call -> OrdinaryToPrimitive -> valueOf -> ThisTimeValue) by directly returning the date's numeric value. Eliminates ~12 method calls per Date-to-number conversion. 2. DateConstructor.Construct: Skip OrdinaryCreateFromConstructor for new Date() When newTarget is the built-in DateConstructor (the common case), directly construct JsDate with the known prototype instead of going through GetPrototypeFromConstructor which does a property lookup. 3. Engine.GetValue: Direct ObjectInstance cast When baseValue.IsObject() is already confirmed, use a direct cast instead of TypeConverter.ToObject() which performs a redundant type check. StopwatchBenchmark results (ShortRun): | Variant | Before | After | Improvement | |----------------------|----------|----------|-------------| | Classic Execute | 251.4 ms | 225.3 ms | -10.4% | | Classic ParsedScript | 264.3 ms | 231.0 ms | -12.6% | | Modern Execute | 302.6 ms | 271.9 ms | -10.1% | | Modern ParsedScript | 320.2 ms | 277.8 ms | -13.2% | Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a direct value path in JintMemberExpression.GetValue for common non-optional obj.prop reads with pre-determined string property names. When base resolves to ObjectInstance, this bypasses Reference pool rent/ resolve round-trip and reads property directly via ObjectInstance.Get. Initialization logic is also shared through EnsureInitialized(). StopwatchBenchmark (ShortRun, before vs after this commit): - Execute (Modern=false): 290.3 ms -> 245.6 ms (-15.4%) - Execute (Modern=true): 306.9 ms -> 301.9 ms (-1.6%) - Execute_ParsedScript (Modern=false): 256.2 ms -> 255.0 ms (-0.5%) - Execute_ParsedScript (Modern=true): 317.6 ms -> 320.5 ms (+0.9%) - Aggregate of all four means: 1171.0 ms -> 1123.0 ms (-4.1%) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…iptor is data Optimize IConstructor.Construct for ScriptFunction base constructors by bypassing OrdinaryCreateFromConstructor when all of these hold: - newTarget is the function itself - prototype descriptor exists - prototype descriptor is not an accessor descriptor In this case we can create JsObject directly and assign prototype from the data descriptor, avoiding constructor property lookup and helper indirection. Accessor prototype descriptors still go through the full path to preserve observable getter semantics. StopwatchBenchmark (ShortRun, step2 -> step3): - Execute_ParsedScript (Modern=false): 255.0 -> 223.9 ms (-12.2%) - Execute_ParsedScript (Modern=true): 320.5 -> 269.2 ms (-16.0%) - Execute (Modern=false): 245.6 -> 291.0 ms (+18.5%) - Execute (Modern=true): 301.9 -> 272.4 ms (-9.8%) - Aggregate of all four means: 1123.0 -> 1056.5 ms (-5.9%) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rExpression Add a lightweight per-node cache for identifiers that resolve in the current lexical environment. For repeated accesses in the same environment and strict-mode context, this: - skips full scope-chain walks in EvaluateInternal - skips full TryGetIdentifierEnvironmentWithBindingValue in GetValue - falls back to full lookup when environment or strictness differs Cache is deliberately scoped to current-environment bindings to preserve correctness under shadowing and dynamic scope changes. StopwatchBenchmark (ShortRun, step3 -> step4): - Execute (Modern=false): 291.0 -> 222.6 ms (-23.5%) - Execute (Modern=true): 272.4 -> 267.7 ms (-1.7%) - Execute_ParsedScript (Modern=false): 223.9 -> 221.2 ms (-1.2%) - Execute_ParsedScript (Modern=true): 269.2 -> 281.3 ms (+4.5%) - Aggregate of all four means: 1056.5 -> 992.8 ms (-6.0%) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend JintMemberExpression direct read path with a small monomorphic cache: - cache (object instance, property descriptor) for non-configurable own props - on cache hit, unwrap cached descriptor directly - on miss, fallback to normal own-property/prototype path Caching is intentionally restricted to non-configurable own properties to keep semantics safe under delete/reconfigure operations. StopwatchBenchmark (ShortRun, step4 -> step5): - Execute (Modern=false): 222.6 -> 216.9 ms (-2.6%) - Execute (Modern=true): 267.7 -> 274.3 ms (+2.5%) - Execute_ParsedScript (Modern=false): 221.2 -> 228.0 ms (+3.1%) - Execute_ParsedScript (Modern=true): 281.3 -> 272.7 ms (-3.1%) - Aggregate of all four means: 992.8 -> 991.9 ms (-0.1%) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reduce HybridDictionary list-to-dictionary cutover from 9 to 6 entries so 6-property objects switch to hash lookups sooner, targeting stopwatch member-access hotspots. Stopwatch Benchmark (default job, A/B average across two runs): -2.65% total mean. Dromaeo total: +0.58% (noise-level). SunSpider total: -1.40%. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restrict the own-property member-read inline cache to plain objects only so Proxy and other exotic objects still go through the full [[Get]] semantics. Restore HybridDictionary cutover constants back to 9 to avoid dictionary-backed enumeration order regressions in for-in tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lahma
marked this pull request as ready for review
March 1, 2026 17:15
lahma
enabled auto-merge (squash)
March 1, 2026 17:15
This was referenced Mar 2, 2026
This was referenced Jun 8, 2026
This was referenced Jun 29, 2026
This was referenced Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR now includes a broader set of performance optimizations plus correctness fixes after regression triage.
What changed
TypeConverter.ToNumeric: direct numeric extraction forJsDateDateConstructor.Construct: avoid redundant prototype lookup whennewTarget === DatenewTarget === this)OrdinaryCreateFromConstructorbypasses in hot built-ins (Array,RegExp,Map,Set,Promise)Engine.GetValue: directObjectInstancecast on already-object valuesJintIdentifierExpressionJintMemberExpressionHybridDictionarycutover constants to avoidfor-inordering regressionsPerformance comparison vs
mainJint.Benchmark.DromaeoBenchmark
Jint.Benchmark.StopwatchBenchmark
Jint.Benchmark.SunSpiderBenchmark