Skip the now binding when no program references it#461
Open
snuderl wants to merge 1 commit intobufbuild:mainfrom
Open
Skip the now binding when no program references it#461snuderl wants to merge 1 commit intobufbuild:mainfrom
snuderl wants to merge 1 commit intobufbuild:mainfrom
Conversation
Variable.newThisVariable always allocated three objects per field evaluation (a NowVariable, the this Variable, and a hierarchical wrapper). Almost no rule expression references the now identifier, so the wrapping is pure waste in the common case. Walk each compiled AST once at build time to record whether it references now. CelPrograms aggregates that flag across its program list and, when nothing needs now, asks Variable for a single unwrapped resolver — three allocations become one per field eval.
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.
Summary
Variable.newThisVariablealways allocated three objects per field evaluation: aNowVariable, thethisVariable, and ahierarchicalVariableResolverwrapping them. Almost no rule expression references thenowidentifier, so the wrapper layer is pure waste in the common case.This PR walks each compiled CEL AST once at build time (via
CelNavigableAst) and records whether it referencesnow.CelProgramsaggregates that flag across its program list at construction; when nothing in the list needsnow,Variable.newThisVariablereturns a single bareVariable("this", val)instead of the three-object stack — three allocations become one per field eval.The flag lives on
CompiledProgramso future construction sites (e.g. user-definedcelrules) get the same treatment automatically.Benchmark results
Steady-state validate() benchmarks, measured with a standalone
ThreadMXBean-based runner (5 forks × 200K iterations each, after a 250K-iteration warmup) so we can report CPU time alongside wall time and confirm the savings are real CPU work, not just GC offload.cpu/wall ≈ 0.99across the board (these benchmarks are fully CPU-bound), and the CPU delta tracks the wall delta within ~0.2pp. The largest relative win is onvalidateManyUnruled— schemas with many fields and few rules are common, and the per-field wrapper overhead is a bigger fraction of total work there.Test plan
./gradlew test— full unit + conformance suite passes.mainbaseline.