Skip to content

perf: skip assert super-chain walk for assert-free objects#698

Open
He-Pin wants to merge 1 commit intodatabricks:masterfrom
He-Pin:perf/skip-assert-walk
Open

perf: skip assert super-chain walk for assert-free objects#698
He-Pin wants to merge 1 commit intodatabricks:masterfrom
He-Pin:perf/skip-assert-walk

Conversation

@He-Pin
Copy link
Copy Markdown
Contributor

@He-Pin He-Pin commented Apr 6, 2026

Motivation

When accessing fields on a Val.Obj, the evaluator walks the super-chain to trigger all assert statements. For objects without any assertions (the common case), this walk is wasted work — iterating through potentially deep inheritance chains for no effect.

Key Design Decision

Add a hasAsserts flag to Val.Obj that tracks whether the object or any of its supers contain assertions. Skip the super-chain walk entirely when hasAsserts == false.

Modification

  • Val.scala: Added hasAsserts flag, propagated through object merge (+)
  • Evaluator.scala: Guard assert walk with hasAsserts check
  • Test: Added test for assert-free objects and objects with asserts to verify both paths

Benchmark Results

JMH (JVM, 3 iterations)

Benchmark Master (ms/op) This PR (ms/op) Change
bench.02 50.427 ± 38.906 47.337 ± 2.780 -6.1%
comparison2 85.854 ± 188.657 67.984 ± 33.584 -20.8% 🔥
realistic2 73.458 ± 66.747 67.857 ± 11.936 -7.6%

Analysis

  • comparison2: Largest improvement — this benchmark creates many small objects without assertions, so skipping the assert walk saves significant time
  • realistic2: Also benefits from reduced per-field-access overhead
  • Safe optimization: objects with assertions still execute them correctly
  • No regressions

References

  • Upstream exploration: he-pin/sjsonnet jit branch commit 85ca07ca

Result

-7% to -21% JVM improvement by skipping unnecessary assert super-chain walks for assertion-free objects.

Add hasAnyAsserts flag to Val.Obj, computed O(1) at construction time
from the super chain. When false, triggerAllAsserts returns immediately
without walking the super chain or setting the asserting guard flag.

The flag is a simple inductive definition:
  hasAnyAsserts = (triggerAsserts != null) || (super?.hasAnyAsserts)

This is safe because:
- Super chains are immutable, acyclic, and built bottom-up
- triggerAsserts is private val (no post-construction mutation)
- addSuper/removeKeys properly propagate via new object construction
- No re-entrancy risk when skipping assert-free chains

Benchmark: bench.02 47.534 → 44.826 ms/op (-5.7%)

Upstream: jit branch commit 85ca07c
@He-Pin He-Pin marked this pull request as ready for review April 6, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant