use aspect_core::{Aspect, AspectError};
use aspect_runtime::{ChainSlot, Composition};
use aspect_std::{
ActionAuditTrailAspect, AuditContext, LoggingAspect, RateLimitAspect, TokenBudgetAspect,
TokenCharge, ToolCall, ToolScopeSandboxAspect,
};
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::any::Any;
use std::time::Duration;
#[inline(never)]
fn dispatch_body(arg: u64) -> Result<u64, AspectError> {
Ok(black_box(arg.wrapping_add(1)))
}
fn sandbox_seeded() -> ToolScopeSandboxAspect {
let s = ToolScopeSandboxAspect::new();
s.allow("bench", ["dispatch"]);
s
}
fn budget_seeded() -> TokenBudgetAspect {
let b = TokenBudgetAspect::new();
b.set_budget("bench", u64::MAX);
b
}
fn bind_all(
sandbox: &ToolScopeSandboxAspect,
budget: &TokenBudgetAspect,
audit: &ActionAuditTrailAspect,
tool: &str,
) -> (u64, u64, u64) {
let k_s = sandbox.bind(ToolCall {
session: "bench".into(),
tool: tool.into(),
});
let k_b = budget.bind(TokenCharge {
session: "bench".into(),
estimated: 1,
});
let k_a = audit.bind(AuditContext {
session: "bench".into(),
tool: tool.into(),
});
(k_s, k_b, k_a)
}
fn run_single_aspect<A: Aspect + 'static>(name: &'static str, aspect: A, arg: u64) {
let chain = Composition::new(vec![ChainSlot::new(name, aspect)]);
let _ = chain.run("dispatch", move || dispatch_body(arg));
}
fn bench_baseline(c: &mut Criterion) {
let mut group = c.benchmark_group("rq3_baseline");
group.throughput(Throughput::Elements(1));
group.bench_function("dispatch_body", |b| {
b.iter(|| dispatch_body(black_box(7)))
});
let empty = Composition::new(vec![]);
group.bench_function("empty_chain", |b| {
b.iter(|| {
let _ = empty.run("dispatch", || dispatch_body(black_box(7)));
})
});
group.finish();
}
fn bench_single_aspect(c: &mut Criterion) {
let mut group = c.benchmark_group("rq3_single_aspect");
group.throughput(Throughput::Elements(1));
group.bench_function("ratelimiter", |b| {
let rl = RateLimitAspect::new(u64::MAX, Duration::from_secs(1));
b.iter(|| run_single_aspect("RateLimiter", rl.clone(), black_box(7)));
});
group.bench_function("logger", |b| {
let lg = LoggingAspect::new();
b.iter(|| run_single_aspect("StructuredLogger", lg.clone(), black_box(7)));
});
group.bench_function("sandbox", |b| {
let sb = sandbox_seeded();
b.iter(|| {
let key = sb.bind(ToolCall {
session: "bench".into(),
tool: "dispatch".into(),
});
run_single_aspect("ToolScopeSandbox", sb.clone(), black_box(7));
sb.release(key);
});
});
group.bench_function("token_budget", |b| {
let bd = budget_seeded();
b.iter(|| {
let key = bd.bind(TokenCharge {
session: "bench".into(),
estimated: 1,
});
run_single_aspect("TokenBudget", bd.clone(), black_box(7));
bd.release(key);
});
});
group.bench_function("audit", |b| {
let ad = ActionAuditTrailAspect::new();
b.iter(|| {
let key = ad.bind(AuditContext {
session: "bench".into(),
tool: "dispatch".into(),
});
run_single_aspect("ActionAuditTrail", ad.clone(), black_box(7));
ad.release(key);
});
});
group.finish();
}
fn bench_chain(c: &mut Criterion) {
let mut group = c.benchmark_group("rq3_chain");
group.throughput(Throughput::Elements(1));
group.bench_function(BenchmarkId::new("depth", 2), |b| {
let sb = sandbox_seeded();
let rl = RateLimitAspect::new(u64::MAX, Duration::from_secs(1));
b.iter(|| {
let k_s = sb.bind(ToolCall {
session: "bench".into(),
tool: "dispatch".into(),
});
let chain = Composition::new(vec![
ChainSlot::new("ToolScopeSandbox", sb.clone()),
ChainSlot::new("RateLimiter", rl.clone()),
]);
let _ = chain.run("dispatch", || dispatch_body(black_box(7)));
sb.release(k_s);
});
});
group.bench_function(BenchmarkId::new("depth", 3), |b| {
let sb = sandbox_seeded();
let rl = RateLimitAspect::new(u64::MAX, Duration::from_secs(1));
let ad = ActionAuditTrailAspect::new();
b.iter(|| {
let k_s = sb.bind(ToolCall {
session: "bench".into(),
tool: "dispatch".into(),
});
let k_a = ad.bind(AuditContext {
session: "bench".into(),
tool: "dispatch".into(),
});
let chain = Composition::new(vec![
ChainSlot::new("ToolScopeSandbox", sb.clone()),
ChainSlot::new("RateLimiter", rl.clone()),
ChainSlot::new("ActionAuditTrail", ad.clone()),
]);
let _ = chain.run("dispatch", || dispatch_body(black_box(7)));
sb.release(k_s);
ad.release(k_a);
});
});
group.bench_function(BenchmarkId::new("depth", 4), |b| {
let sb = sandbox_seeded();
let rl = RateLimitAspect::new(u64::MAX, Duration::from_secs(1));
let ad = ActionAuditTrailAspect::new();
let lg = LoggingAspect::new();
b.iter(|| {
let k_s = sb.bind(ToolCall {
session: "bench".into(),
tool: "dispatch".into(),
});
let k_a = ad.bind(AuditContext {
session: "bench".into(),
tool: "dispatch".into(),
});
let chain = Composition::new(vec![
ChainSlot::new("ToolScopeSandbox", sb.clone()),
ChainSlot::new("RateLimiter", rl.clone()),
ChainSlot::new("ActionAuditTrail", ad.clone()),
ChainSlot::new("StructuredLogger", lg.clone()),
]);
let _ = chain.run("dispatch", || dispatch_body(black_box(7)));
sb.release(k_s);
ad.release(k_a);
});
});
group.bench_function(BenchmarkId::new("depth", 5), |b| {
let sb = sandbox_seeded();
let rl = RateLimitAspect::new(u64::MAX, Duration::from_secs(1));
let bd = budget_seeded();
let ad = ActionAuditTrailAspect::new();
let lg = LoggingAspect::new();
b.iter(|| {
let (k_s, k_b, k_a) = bind_all(&sb, &bd, &ad, "dispatch");
let chain = Composition::new(vec![
ChainSlot::new("ToolScopeSandbox", sb.clone()),
ChainSlot::new("RateLimiter", rl.clone()),
ChainSlot::new("TokenBudget", bd.clone()),
ChainSlot::new("ActionAuditTrail", ad.clone()),
ChainSlot::new("StructuredLogger", lg.clone()),
]);
let _ = chain.run("dispatch", || dispatch_body(black_box(7)));
sb.release(k_s);
bd.release(k_b);
ad.release(k_a);
});
});
group.finish();
}
fn bench_denied(c: &mut Criterion) {
let mut group = c.benchmark_group("rq3_denied");
group.throughput(Throughput::Elements(1));
group.bench_function("sandbox_denies_4chain", |b| {
let sb = sandbox_seeded();
let rl = RateLimitAspect::new(u64::MAX, Duration::from_secs(1));
let ad = ActionAuditTrailAspect::new();
let lg = LoggingAspect::new();
b.iter(|| {
let k_s = sb.bind(ToolCall {
session: "bench".into(),
tool: "out_of_scope".into(),
});
let chain = Composition::new(vec![
ChainSlot::new("ToolScopeSandbox", sb.clone()),
ChainSlot::new("RateLimiter", rl.clone()),
ChainSlot::new("ActionAuditTrail", ad.clone()),
ChainSlot::new("StructuredLogger", lg.clone()),
]);
let _ = chain.run("dispatch", || dispatch_body(black_box(7)));
sb.release(k_s);
});
});
group.finish();
}
criterion_group!(
benches,
bench_baseline,
bench_single_aspect,
bench_chain,
bench_denied
);
criterion_main!(benches);
#[allow(dead_code)]
fn _keep_any_in_scope(_: Box<dyn Any>) {}