Skip to main content

lean_ctx/core/context_kernel/
perf_benchmark.rs

1#[cfg(test)]
2mod tests {
3    use std::sync::MutexGuard;
4    use std::time::{Duration, Instant};
5
6    use crate::core::context_kernel::{
7        adaptive_bridge, adaptive_hook, ctx_read_dedup, evidence_hook, evidence_wiring, health,
8        kernel_config, list_tools_opt, response_evidence, startup, token_envelope::TokenEnvelope,
9    };
10    use crate::tools::{search_hook, search_kernel};
11
12    /// Wall-clock budgets below only hold on a quiet, single-threaded runner.
13    /// The suite runs multi-threaded, so they are opt-in: CI's perf-gate step
14    /// sets `LEAN_CTX_PERF_GATE=1` and runs them serialized. The work itself
15    /// (and every correctness assertion) still runs on every invocation.
16    fn timing_enforced() -> bool {
17        std::env::var("LEAN_CTX_PERF_GATE").as_deref() == Ok("1")
18    }
19
20    fn isolated() -> MutexGuard<'static, ()> {
21        let guard = kernel_config::KERNEL_TEST_LOCK
22            .lock()
23            .unwrap_or_else(std::sync::PoisonError::into_inner);
24        kernel_config::reset_features();
25        crate::core::context_kernel::dedup_wiring::reset_dedup();
26        crate::core::context_kernel::schema_wiring::reset_schema_state();
27        crate::core::context_kernel::envelope_wiring::reset_evidence();
28        crate::core::context_kernel::proxy_bridge::reset_state();
29        crate::core::context_kernel::mcp_bridge::reset_mcp_state();
30        evidence_wiring::reset();
31        adaptive_bridge::reset();
32        search_kernel::reset();
33        crate::core::context_kernel::usage_normalizer::reset_usage();
34        crate::core::context_kernel::receipt_chain::reset_chain();
35        response_evidence::reset();
36        search_hook::reset();
37        adaptive_hook::reset();
38        startup::reset();
39        ctx_read_dedup::reset();
40        list_tools_opt::reset();
41        guard
42    }
43
44    #[test]
45    fn dedup_throughput() {
46        let _guard = isolated();
47        let started = Instant::now();
48        for index in 0..1_000 {
49            let content = format!("unique content {index}");
50            assert!(ctx_read_dedup::try_dedup("bench.rs", &content).is_none());
51        }
52        assert!(!timing_enforced() || started.elapsed() < Duration::from_millis(500));
53    }
54
55    #[test]
56    fn evidence_throughput() {
57        let _guard = isolated();
58        let started = Instant::now();
59        for _ in 0..10_000 {
60            evidence_hook::record_tool_call("ctx_read", 100, 25);
61        }
62        assert!(!timing_enforced() || started.elapsed() < Duration::from_millis(200));
63        assert_eq!(evidence_hook::evidence_report().tool_calls, 10_000);
64    }
65
66    #[test]
67    fn health_latency() {
68        let _guard = isolated();
69        let started = Instant::now();
70        for _ in 0..100 {
71            std::hint::black_box(health::kernel_health());
72        }
73        assert!(!timing_enforced() || started.elapsed() < Duration::from_millis(50));
74    }
75
76    #[test]
77    fn search_dedup_scales() {
78        let _guard = isolated();
79        let started = Instant::now();
80        for index in 0..500 {
81            search_kernel::record_search(&format!("query-{index}"), 5, 100);
82        }
83        for index in 0..100 {
84            search_kernel::record_search(&format!("query-{index}"), 5, 100);
85        }
86        let summary = search_kernel::search_summary();
87        assert!(!timing_enforced() || started.elapsed() < Duration::from_millis(200));
88        assert_eq!(summary.total_searches, 600);
89        assert_eq!(summary.unique_queries, 500);
90        assert_eq!(summary.repeated_queries, 100);
91    }
92
93    #[test]
94    fn envelope_creation_speed() {
95        let _guard = isolated();
96        let started = Instant::now();
97        let mut token_total = 0;
98        for index in 0..5_000 {
99            let envelope = TokenEnvelope {
100                model: "benchmark-model".to_owned(),
101                input_tokens: index,
102                output_tokens: 10,
103                cache_read_tokens: 5,
104                ..TokenEnvelope::default()
105            };
106            token_total += std::hint::black_box(envelope).input_tokens;
107        }
108        assert!(!timing_enforced() || started.elapsed() < Duration::from_millis(50));
109        assert_eq!(token_total, 12_497_500);
110    }
111
112    #[test]
113    fn concurrent_evidence_safe() {
114        let _guard = isolated();
115        let threads: Vec<_> = (0..4)
116            .map(|_| {
117                std::thread::spawn(|| {
118                    for _ in 0..1_000 {
119                        evidence_hook::record_tool_call("ctx_shell", 20, 10);
120                    }
121                })
122            })
123            .collect();
124        for thread in threads {
125            thread.join().expect("evidence worker panicked");
126        }
127        assert_eq!(evidence_hook::evidence_report().tool_calls, 4_000);
128    }
129
130    #[test]
131    fn schema_opt_throughput() {
132        let _guard = isolated();
133        let tools: Vec<_> = (0..20)
134            .map(|index| {
135                (
136                    format!("tool-{index}"),
137                    "Read files and return complete file contents with metadata".to_owned(),
138                    0_usize,
139                )
140            })
141            .collect();
142        let started = Instant::now();
143        for _ in 0..100 {
144            std::hint::black_box(list_tools_opt::optimize_descriptions(
145                tools.clone(),
146                "cursor",
147            ));
148        }
149        assert!(!timing_enforced() || started.elapsed() < Duration::from_millis(500));
150    }
151}