perf: share compiled RegexSet cache across scanners#665
perf: share compiled RegexSet cache across scanners#665debugactiveprocess wants to merge 2 commits into
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
d1e43b9 to
423e7e1
Compare
| /// level avoids rebuilding the same `RegexSet` once per scanner/thread. | ||
| #[serde(skip)] | ||
| pub(in crate::compiler) compiled_regex_sets: | ||
| OnceLock<FxHashMap<RegexSetId, OnceLock<regex::bytes::RegexSet>>>, |
There was a problem hiding this comment.
Is this double-lock really necessary? It looks very suspicious that we need a lock for accessing the hash map and then another lock for accessing the RegexSet.
There was a problem hiding this comment.
good point, I remove the outer OnceLock and now initialize the runtime cache when Rules is built or deserialized. Each RegexSet still has its own OnceLock for lazy compilation, but get_regex_set no longer needs to lazily initialize and lock the whole cache map.
Optimize RegexSet Compilation and Reuse Across Scanners
Overview
This PR improves the lifecycle management of
RegexSetcompilation by moving compiled automata from scanner-local caches to a shared cache associated withRules.Previously,
RegexSetinstances were lazily compiled inside eachScannerthroughScanContext::regex_set_matches(). While this avoided unnecessary upfront work, it introduced duplicated compilation costs when multiple scanners operated on the same ruleset.Previous Behavior
When a
RegexSetwas first used during scanning:CompiledRules::get_regex_set()was invoked.regex::bytes::RegexSetBuilderreparsed those strings and built a new automaton.This workflow resulted in:
RegexSetcompilation across scanners.New Approach
Compiled
RegexSetinstances are now stored at theRuleslevel and shared across scanners.Conceptually:
The first scanner requesting a given
RegexSetperforms the compilation and stores the result in a shared cache. Subsequent scanners reuse the already-built automaton through anArc, eliminating redundant compilation work.Benefits
Reduced Cold-Scan Latency
The first file that triggers a grouped regex no longer incurs repeated compilation costs across scanners.
Shared Automata Across Threads
Parallel scans executed by the CLI can reuse the same compiled
RegexSetinstead of rebuilding identical automata in multiple workers.Lower CPU Consumption
Compilation is performed once per ruleset rather than once per scanner instance.
Better Scalability for Long-Lived Services
Applications that create multiple scanners from the same
Rulesobject benefit from improved reuse and lower memory churn.Future Opportunities
This change lays the groundwork for additional optimizations around regex compilation.
A remaining source of overhead is the current workflow:
Future work could explore direct construction of grouped automata from HIR representations, similar to how individual regexes can be compiled through:
Avoiding reparsing would further reduce allocations and compilation overhead for large regex groups.
Expected Impact
Workloads with:
matchesshould observe lower startup overhead, reduced CPU utilization, and improved scan throughput due to more efficient automata reuse.