mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
This change integrates mimalloc as the default memory allocator for Regorus, delivering significant performance improvements across all evaluation modes and language bindings. Technical Implementation: - Build mimalloc in vendored mode from C sources (following QSharp approach) - Implement GlobalAlloc trait for seamless Rust integration - Add optional 'mimalloc' feature flag for conditional compilation - Add comprehensive ACI benchmarks to measure evaluation performance Performance Impact: Rust Engine Evaluation: - Single-threaded: ~29% improvement (423 vs 328 Kelem/s) - Multi-threaded: Better scaling with reduced thread contention - Fresh engines: ~24% improvement (56 vs 45 Kelem/s) Rust Compiled Policy Evaluation: - Single-threaded: ~41% improvement (426 vs 303 Kelem/s) - Multi-threaded: Improved allocation efficiency under contention - Fresh compilation: ~26% improvement (53 vs 42 Kelem/s) C# FFI Bindings: - Engine evaluation: ~27% improvement (279 vs 219 Kelem/s) - Compiled policies: ~29% improvement (273 vs 211 Kelem/s) - Better threading characteristics through improved underlying allocation Key Benefits: - Reduced allocation-related contention in multi-threaded scenarios - More consistent performance across different thread counts - Improved memory allocation efficiency for both native Rust and FFI workloads - Better scaling characteristics for production deployments The mimalloc integration provides substantial performance gains while maintaining full compatibility with existing code through feature flags. Reference: QSharp allocator implementation (https://github.com/microsoft/qsharp/tree/main/source/allocator) Fixes #297 Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
81 lines
2.5 KiB
Rust
81 lines
2.5 KiB
Rust
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
use regorus::{Engine, Value};
|
|
|
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
|
use serde::{Deserialize, Serialize};
|
|
use walkdir::WalkDir;
|
|
|
|
use std::path::Path;
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|
struct TestCase {
|
|
note: String,
|
|
data: Value,
|
|
input: Value,
|
|
modules: Vec<String>,
|
|
query: String,
|
|
want_result: Value,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|
struct YamlTest {
|
|
cases: Vec<TestCase>,
|
|
}
|
|
|
|
fn aci_policy_eval(c: &mut Criterion) {
|
|
let dir = Path::new("tests/aci");
|
|
for entry in WalkDir::new(dir)
|
|
.sort_by_file_name()
|
|
.into_iter()
|
|
.filter_map(|e| e.ok())
|
|
{
|
|
let path = entry.path();
|
|
if !path.to_string_lossy().ends_with(".yaml") {
|
|
continue;
|
|
}
|
|
|
|
let yaml = std::fs::read(path).expect("failed to read yaml test");
|
|
let yaml = String::from_utf8_lossy(&yaml);
|
|
let test: YamlTest = serde_yaml::from_str(&yaml).expect("failed to deserialize yaml test");
|
|
|
|
for case in &test.cases {
|
|
let rule = case.query.replace("=x", "");
|
|
c.bench_with_input(
|
|
BenchmarkId::new("case ", format!("{} {}", &case.note, &rule)),
|
|
&case,
|
|
|b, case| {
|
|
let mut engine = Engine::new();
|
|
engine.set_rego_v0(true);
|
|
|
|
engine
|
|
.add_data(case.data.clone())
|
|
.expect("failed to add data");
|
|
engine.set_input(case.input.clone());
|
|
|
|
for (idx, rego) in case.modules.iter().enumerate() {
|
|
if rego.ends_with(".rego") {
|
|
let path = dir.join(rego);
|
|
let path = path.to_str().expect("not a valid path");
|
|
engine
|
|
.add_policy_from_file(path)
|
|
.expect("failed to add policy");
|
|
} else {
|
|
engine
|
|
.add_policy(format!("rego{idx}.rego"), rego.clone())
|
|
.expect("failed to add policy");
|
|
}
|
|
}
|
|
|
|
b.iter(|| {
|
|
engine.eval_rule(rule.clone()).unwrap();
|
|
})
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
criterion_group!(aci_benches, aci_policy_eval);
|
|
criterion_main!(aci_benches);
|