use rust_rule_engine::engine::facts::Facts;
use rust_rule_engine::engine::knowledge_base::KnowledgeBase;
use rust_rule_engine::engine::parallel::{ParallelConfig, ParallelRuleEngine};
use rust_rule_engine::engine::rule::{Condition, ConditionGroup, Rule};
use rust_rule_engine::engine::RustRuleEngine;
use rust_rule_engine::rete::auto_network::{
Condition as AutoCondition, ConditionGroup as AutoConditionGroup,
};
use rust_rule_engine::rete::network::build_rete_ul_from_condition_group;
use rust_rule_engine::rete::{FactValue, IncrementalEngine, TypedFacts, TypedReteUlRule};
use rust_rule_engine::types::{ActionType, Operator, Value};
use std::time::Instant;
fn main() {
println!("🏁 Quick Benchmark: Native vs Parallel vs RETE");
println!("================================================\n");
for rule_count in [10, 50, 100, 200, 500, 1000] {
println!("📊 Testing with {} rules:", rule_count);
run_comparison(rule_count);
println!();
}
}
fn run_comparison(rule_count: usize) {
let iterations = 100;
let (mut native_engine, native_facts) = setup_native(rule_count);
let (parallel_engine, parallel_kb, parallel_facts) = setup_parallel(rule_count);
let _ = native_engine.execute(&native_facts);
let _ = parallel_engine.execute_parallel(¶llel_kb, ¶llel_facts, false);
let start = Instant::now();
for _ in 0..iterations {
let _ = native_engine.execute(&native_facts);
}
let native_time = start.elapsed();
let native_avg = native_time.as_micros() / iterations;
let start = Instant::now();
for _ in 0..iterations {
let _ = parallel_engine.execute_parallel(¶llel_kb, ¶llel_facts, false);
}
let parallel_time = start.elapsed();
let parallel_avg = parallel_time.as_micros() / iterations;
let start = Instant::now();
for _ in 0..iterations {
let mut engine = setup_rete(rule_count);
let mut facts = TypedFacts::new();
facts.set("User.age", FactValue::Integer(35));
facts.set("User.score", FactValue::Integer(85));
facts.set("User.premium", FactValue::Boolean(true));
engine.insert("User".to_string(), facts); }
let rete_time = start.elapsed();
let rete_avg = rete_time.as_micros() / iterations;
let parallel_speedup = native_avg as f64 / parallel_avg as f64;
let rete_speedup = native_avg as f64 / rete_avg as f64;
println!(" Native: {:6} μs", native_avg);
println!(
" Parallel: {:6} μs ({:.2}x faster)",
parallel_avg, parallel_speedup
);
if rete_speedup > 1.0 {
println!(
" RETE: {:6} μs ({:.2}x faster) ⚡",
rete_avg, rete_speedup
);
} else {
println!(
" RETE: {:6} μs ({:.2}x slower) ⏱",
rete_avg,
1.0 / rete_speedup
);
}
}
fn setup_native(rule_count: usize) -> (RustRuleEngine, Facts) {
let kb = KnowledgeBase::new("Native");
for i in 0..rule_count {
let condition = match i % 4 {
0 => {
ConditionGroup::single(Condition::new(
"User.age".to_string(),
Operator::GreaterThan,
Value::Integer(18 + (i % 30) as i64),
))
}
1 => {
ConditionGroup::and(
ConditionGroup::single(Condition::new(
"User.age".to_string(),
Operator::GreaterThan,
Value::Integer(25),
)),
ConditionGroup::single(Condition::new(
"User.score".to_string(),
Operator::GreaterThanOrEqual,
Value::Integer(70 + (i % 30) as i64),
)),
)
}
2 => {
ConditionGroup::or(
ConditionGroup::single(Condition::new(
"User.premium".to_string(),
Operator::Equal,
Value::Boolean(true),
)),
ConditionGroup::single(Condition::new(
"User.score".to_string(),
Operator::GreaterThan,
Value::Integer(90),
)),
)
}
_ => {
ConditionGroup::or(
ConditionGroup::and(
ConditionGroup::single(Condition::new(
"User.age".to_string(),
Operator::GreaterThan,
Value::Integer(30),
)),
ConditionGroup::single(Condition::new(
"User.score".to_string(),
Operator::GreaterThan,
Value::Integer(80),
)),
),
ConditionGroup::single(Condition::new(
"User.premium".to_string(),
Operator::Equal,
Value::Boolean(true),
)),
)
}
};
let rule = Rule::new(
format!("Rule{}", i),
condition,
vec![ActionType::Set {
field: format!("result{}", i),
value: Value::Boolean(true),
}],
);
let _ = kb.add_rule(rule);
}
let engine = RustRuleEngine::new(kb);
let facts = Facts::new();
facts.set("User.age", Value::Integer(35));
facts.set("User.score", Value::Integer(85));
facts.set("User.premium", Value::Boolean(true));
(engine, facts)
}
fn setup_parallel(rule_count: usize) -> (ParallelRuleEngine, KnowledgeBase, Facts) {
let config = ParallelConfig::default();
let engine = ParallelRuleEngine::new(config);
let kb = KnowledgeBase::new("Parallel");
for i in 0..rule_count {
let condition = match i % 4 {
0 => ConditionGroup::single(Condition::new(
"User.age".to_string(),
Operator::GreaterThan,
Value::Integer(18 + (i % 30) as i64),
)),
1 => ConditionGroup::and(
ConditionGroup::single(Condition::new(
"User.age".to_string(),
Operator::GreaterThan,
Value::Integer(25),
)),
ConditionGroup::single(Condition::new(
"User.score".to_string(),
Operator::GreaterThanOrEqual,
Value::Integer(70 + (i % 30) as i64),
)),
),
2 => ConditionGroup::or(
ConditionGroup::single(Condition::new(
"User.premium".to_string(),
Operator::Equal,
Value::Boolean(true),
)),
ConditionGroup::single(Condition::new(
"User.score".to_string(),
Operator::GreaterThan,
Value::Integer(90),
)),
),
_ => ConditionGroup::or(
ConditionGroup::and(
ConditionGroup::single(Condition::new(
"User.age".to_string(),
Operator::GreaterThan,
Value::Integer(30),
)),
ConditionGroup::single(Condition::new(
"User.score".to_string(),
Operator::GreaterThan,
Value::Integer(80),
)),
),
ConditionGroup::single(Condition::new(
"User.premium".to_string(),
Operator::Equal,
Value::Boolean(true),
)),
),
};
let rule = Rule::new(
format!("Rule{}", i),
condition,
vec![ActionType::Set {
field: format!("result{}", i),
value: Value::Boolean(true),
}],
);
let _ = kb.add_rule(rule);
}
let facts = Facts::new();
facts.set("User.age", Value::Integer(35));
facts.set("User.score", Value::Integer(85));
facts.set("User.premium", Value::Boolean(true));
(engine, kb, facts)
}
fn setup_rete(rule_count: usize) -> IncrementalEngine {
let mut engine = IncrementalEngine::new();
for i in 0..rule_count {
let condition = match i % 4 {
0 => AutoConditionGroup::Single(AutoCondition {
field: "User.age".to_string(),
operator: ">".to_string(),
value: format!("{}", 18 + (i % 30)),
}),
1 => AutoConditionGroup::Compound {
left: Box::new(AutoConditionGroup::Single(AutoCondition {
field: "User.age".to_string(),
operator: ">".to_string(),
value: "25".to_string(),
})),
operator: "AND".to_string(),
right: Box::new(AutoConditionGroup::Single(AutoCondition {
field: "User.score".to_string(),
operator: ">=".to_string(),
value: format!("{}", 70 + (i % 30)),
})),
},
2 => AutoConditionGroup::Compound {
left: Box::new(AutoConditionGroup::Single(AutoCondition {
field: "User.premium".to_string(),
operator: "==".to_string(),
value: "true".to_string(),
})),
operator: "OR".to_string(),
right: Box::new(AutoConditionGroup::Single(AutoCondition {
field: "User.score".to_string(),
operator: ">".to_string(),
value: "90".to_string(),
})),
},
_ => {
AutoConditionGroup::Compound {
left: Box::new(AutoConditionGroup::Compound {
left: Box::new(AutoConditionGroup::Single(AutoCondition {
field: "User.age".to_string(),
operator: ">".to_string(),
value: "30".to_string(),
})),
operator: "AND".to_string(),
right: Box::new(AutoConditionGroup::Single(AutoCondition {
field: "User.score".to_string(),
operator: ">".to_string(),
value: "80".to_string(),
})),
}),
operator: "OR".to_string(),
right: Box::new(AutoConditionGroup::Single(AutoCondition {
field: "User.premium".to_string(),
operator: "==".to_string(),
value: "true".to_string(),
})),
}
}
};
let node = build_rete_ul_from_condition_group(&condition);
let rule = TypedReteUlRule {
name: format!("Rule{}", i),
node,
priority: 0,
no_loop: false,
action: std::sync::Arc::new(move |facts: &mut TypedFacts, _results: &mut rust_rule_engine::rete::action_result::ActionResults| {
facts.set(format!("result{}", i), FactValue::Boolean(true));
}),
};
engine.add_rule(rule, vec!["User".to_string()]);
}
engine
}