strange_loop/nano_agent/
mod.rs1use crossbeam::queue::ArrayQueue;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
9use std::time::Instant;
10
11pub mod bus;
12pub mod scheduler;
13pub mod agents;
14pub mod budget;
15pub mod critic;
16pub mod optimization;
17
18pub use bus::{NanoBus, Message};
19pub use scheduler::{NanoScheduler, SchedulerConfig};
20pub use budget::{Budget, BudgetGuard};
21pub use critic::{CriticReflector, PolicyUpdate};
22
23pub trait NanoAgent: Send + Sync {
25 fn name(&self) -> &'static str;
27
28 #[inline(always)]
30 fn tick(&mut self, now_ns: u128, bus: &NanoBus) -> TickResult;
31
32 fn budget_ns(&self) -> u128;
34
35 fn reflect(&mut self, _update: PolicyUpdate) {}
37}
38
39#[derive(Debug, Clone, Copy)]
41pub struct TickResult {
42 pub cycles: u64,
43 pub messages_sent: u32,
44 pub messages_recv: u32,
45 pub budget_used_ns: u128,
46}
47
48impl Default for TickResult {
49 fn default() -> Self {
50 Self {
51 cycles: 0,
52 messages_sent: 0,
53 messages_recv: 0,
54 budget_used_ns: 0,
55 }
56 }
57}
58
59#[cfg(target_arch = "x86_64")]
61#[inline(always)]
62pub fn rdtsc() -> u64 {
63 unsafe {
64 std::arch::x86_64::_rdtsc()
65 }
66}
67
68#[cfg(not(target_arch = "x86_64"))]
69#[inline(always)]
70pub fn rdtsc() -> u64 {
71 std::time::Instant::now().elapsed().as_nanos() as u64
73}
74
75#[inline(always)]
77pub fn spin() {
78 std::hint::spin_loop();
79}
80
81#[repr(C, align(64))]
83pub struct AlignedState<T> {
84 pub data: T,
85 _padding: [u8; 0], }
87
88impl<T> AlignedState<T> {
89 pub fn new(data: T) -> Self {
90 Self {
91 data,
92 _padding: [],
93 }
94 }
95}
96
97pub struct NanoMetrics {
99 pub total_ticks: AtomicU64,
100 pub total_cycles: AtomicU64,
101 pub messages_sent: AtomicU64,
102 pub messages_recv: AtomicU64,
103 pub budget_violations: AtomicU64,
104 pub max_latency_ns: AtomicU64,
105}
106
107impl NanoMetrics {
108 pub fn new() -> Self {
109 Self {
110 total_ticks: AtomicU64::new(0),
111 total_cycles: AtomicU64::new(0),
112 messages_sent: AtomicU64::new(0),
113 messages_recv: AtomicU64::new(0),
114 budget_violations: AtomicU64::new(0),
115 max_latency_ns: AtomicU64::new(0),
116 }
117 }
118
119 pub fn record_tick(&self, result: &TickResult) {
120 self.total_ticks.fetch_add(1, Ordering::Relaxed);
121 self.total_cycles.fetch_add(result.cycles, Ordering::Relaxed);
122 self.messages_sent.fetch_add(result.messages_sent as u64, Ordering::Relaxed);
123 self.messages_recv.fetch_add(result.messages_recv as u64, Ordering::Relaxed);
124
125 let mut current_max = self.max_latency_ns.load(Ordering::Relaxed);
127 while result.budget_used_ns as u64 > current_max {
128 match self.max_latency_ns.compare_exchange_weak(
129 current_max,
130 result.budget_used_ns as u64,
131 Ordering::Relaxed,
132 Ordering::Relaxed
133 ) {
134 Ok(_) => break,
135 Err(x) => current_max = x,
136 }
137 }
138 }
139}
140
141#[derive(Debug, Clone, Copy)]
143pub enum SchedulerTopology {
144 RoundRobin,
146 Priority,
148 Hierarchical,
150 Mesh,
152 Quantum,
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 struct TestAgent {
161 counter: u64,
162 }
163
164 impl NanoAgent for TestAgent {
165 fn name(&self) -> &'static str { "test" }
166
167 fn tick(&mut self, _now_ns: u128, _bus: &NanoBus) -> TickResult {
168 self.counter += 1;
169 TickResult::default()
170 }
171
172 fn budget_ns(&self) -> u128 { 1000 } }
174
175 #[test]
176 fn test_aligned_state() {
177 let state = AlignedState::new(42u64);
178 assert_eq!(state.data, 42);
179
180 let ptr = &state as *const _ as usize;
182 assert_eq!(ptr % 64, 0, "State should be cache-line aligned");
183 }
184
185 #[test]
186 fn test_nano_metrics() {
187 let metrics = NanoMetrics::new();
188 let result = TickResult {
189 cycles: 1000,
190 messages_sent: 5,
191 messages_recv: 3,
192 budget_used_ns: 500,
193 };
194
195 metrics.record_tick(&result);
196
197 assert_eq!(metrics.total_ticks.load(Ordering::Relaxed), 1);
198 assert_eq!(metrics.total_cycles.load(Ordering::Relaxed), 1000);
199 assert_eq!(metrics.messages_sent.load(Ordering::Relaxed), 5);
200 assert_eq!(metrics.messages_recv.load(Ordering::Relaxed), 3);
201 assert_eq!(metrics.max_latency_ns.load(Ordering::Relaxed), 500);
202 }
203}