perf: general performance improvements - #500
Merged
Merged
Conversation
ivanmiletic
approved these changes
Aug 3, 2026
This was referenced Aug 4, 2026
Merged
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.
Leaner values
Variableinternals were rebuilt for speed without new complexity: strings use an inline small-string representation (short keys and values never touch the heap, well-known keys like$and$rootare free constants), and objects use a compact insertion-ordered representation that spills to a hash map only when they grow. Small payloads now serialize deterministically. The entire core workspace is#![forbid(unsafe_code)]— an experiment with pooled allocations was measured and deliberately dropped in favor of the simpler design.Scoped evaluation
The expression VM evaluates against a scope of locals layered over the input instead of copying and mutating environment objects. Per-run bindings like
$and$nodesno longer force copy-on-write of the whole environment or leak writes into caller-owned data, and array or scalar environments pass through untouched.Validation without conversion
JSON Schema validation runs directly over
Variabletrees through a safe borrow-guard adapter — no serde conversion per validation. Validators compile once per document and travel with it, as do dictionary-resolved schemas, instead of being rebuilt on every evaluation. Fractional numbers now validate correctly againstintegerand numeric bounds.Indexed decision tables
The equality index the policy engine uses for decision tables now accelerates graph tables too, from one shared implementation. Literal cells are indexed per column at compile time with wildcard and complex cells as fallback rows, so a lookup prunes non-matching rows before any expression runs. Traced runs keep the linear scan, which doubles as a permanent parity oracle, and uncompiled content transparently falls back to it.
Shared compiled content
Graph content is reference-counted end to end: sub-decision calls and repeated evaluations share one compiled artifact — opcodes, table indexes, resolved schemas, validators — instead of cloning per call. This also removes cross-thread contention, so throughput scales close to linearly with cores.
Fixes
v1 function nodes and custom nodes receive
$nodesagain. Expression-node$references now have uniform aliasing semantics instead of a fixed-depth copy. TheToVariablederive works for consumers that depend only on the expression crate and for tagged enums. Cache entries first seen through synchronous loads gain their resolved schemas on first use instead of never.Business Value
Evaluations are faster across the board — lower median latency, a much shorter tail on rule-heavy workloads, and near-linear multi-core scaling — with identical results, verified by parity tests that run the old and new paths against each other. The engine got safer at the same time: no unsafe code anywhere in core, no custom allocator requirements for embedders, and all precompiled state carries a single, predictable lifecycle with its document.