1use super::{NanoAgent, NanoBus, TickResult, NanoMetrics, SchedulerTopology, rdtsc, spin};
4use std::time::{Instant, Duration};
5use std::collections::VecDeque;
6
7pub struct SchedulerConfig {
9 pub topology: SchedulerTopology,
10 pub run_duration_ns: u128,
11 pub tick_duration_ns: u128,
12 pub max_agents: usize,
13 pub bus_capacity: usize,
14 pub enable_tracing: bool,
15}
16
17impl Default for SchedulerConfig {
18 fn default() -> Self {
19 Self {
20 topology: SchedulerTopology::RoundRobin,
21 run_duration_ns: 1_000_000_000, tick_duration_ns: 1_000_000, max_agents: 1024,
24 bus_capacity: 65536,
25 enable_tracing: false,
26 }
27 }
28}
29
30struct AgentSlot {
32 agent: Box<dyn NanoAgent>,
33 budget_ns: u128,
34 priority: u32,
35 metrics: NanoMetrics,
36}
37
38pub struct NanoScheduler {
40 agents: Vec<AgentSlot>,
41 bus: NanoBus,
42 config: SchedulerConfig,
43 start_time: Instant,
44 traces: VecDeque<AgentTrace>,
45}
46
47#[derive(Debug, Clone)]
49pub struct AgentTrace {
50 pub agent_name: &'static str,
51 pub timestamp_ns: u128,
52 pub result: TickResult,
53}
54
55impl NanoScheduler {
56 pub fn new(config: SchedulerConfig) -> Self {
58 let bus = NanoBus::new(config.bus_capacity);
59 Self {
60 agents: Vec::with_capacity(config.max_agents),
61 bus,
62 config,
63 start_time: Instant::now(),
64 traces: VecDeque::with_capacity(10000),
65 }
66 }
67
68 pub fn register<A: NanoAgent + 'static>(&mut self, agent: A) {
70 let budget_ns = agent.budget_ns();
71 let slot = AgentSlot {
72 agent: Box::new(agent),
73 budget_ns,
74 priority: 0,
75 metrics: NanoMetrics::new(),
76 };
77 self.agents.push(slot);
78 }
79
80 pub fn register_with_priority<A: NanoAgent + 'static>(
82 &mut self,
83 agent: A,
84 priority: u32,
85 ) {
86 let budget_ns = agent.budget_ns();
87 let slot = AgentSlot {
88 agent: Box::new(agent),
89 budget_ns,
90 priority,
91 metrics: NanoMetrics::new(),
92 };
93 self.agents.push(slot);
94 }
95
96 pub fn agent_count(&self) -> usize {
98 self.agents.len()
99 }
100
101 pub fn add_agent(&mut self, agent: Box<dyn NanoAgent>) {
103 let budget_ns = agent.budget_ns();
104 let slot = AgentSlot {
105 agent,
106 budget_ns,
107 priority: 0,
108 metrics: NanoMetrics::new(),
109 };
110 self.agents.push(slot);
111 }
112
113 pub fn run(mut self) -> SchedulerStats {
115 let start = Instant::now();
116 let mut now_ns = || start.elapsed().as_nanos();
117 let run_budget = self.config.run_duration_ns;
118
119 if matches!(self.config.topology, SchedulerTopology::Priority) {
121 self.agents.sort_by_key(|a| std::cmp::Reverse(a.priority));
122 }
123
124 let mut total_ticks = 0u64;
125 let mut total_cycles = 0u64;
126 let mut budget_violations = 0u64;
127
128 while now_ns() < run_budget {
130 let macro_tick_start = now_ns();
131
132 match self.config.topology {
134 SchedulerTopology::RoundRobin => {
135 self.execute_round_robin(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
136 }
137 SchedulerTopology::Priority => {
138 self.execute_priority(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
139 }
140 SchedulerTopology::Hierarchical => {
141 self.execute_hierarchical(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
142 }
143 SchedulerTopology::Mesh => {
144 self.execute_mesh(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
145 }
146 SchedulerTopology::Quantum => {
147 self.execute_quantum(|| now_ns(), &mut total_ticks, &mut total_cycles, &mut budget_violations);
148 }
149 }
150
151 while now_ns() - macro_tick_start < self.config.tick_duration_ns {
153 spin();
154 }
155 }
156
157 SchedulerStats {
159 total_ticks,
160 total_cycles,
161 budget_violations,
162 runtime_ns: start.elapsed().as_nanos(),
163 agent_count: self.agents.len(),
164 traces: if self.config.enable_tracing {
165 self.traces.into()
166 } else {
167 Vec::new()
168 },
169 }
170 }
171
172 fn execute_round_robin(
174 &mut self,
175 mut now_ns: impl FnMut() -> u128,
176 total_ticks: &mut u64,
177 total_cycles: &mut u64,
178 budget_violations: &mut u64,
179 ) {
180 for slot in &mut self.agents {
181 let tick_start_ns = now_ns();
182 let tick_start_tsc = rdtsc();
183
184 let bus = self.bus.clone_bus();
186 let result = slot.agent.tick(tick_start_ns, &bus);
187
188 let tick_cycles = rdtsc() - tick_start_tsc;
190 let tick_duration_ns = now_ns() - tick_start_ns;
191
192 if tick_duration_ns > slot.budget_ns {
194 *budget_violations += 1;
195 slot.metrics.budget_violations.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
196 }
197
198 slot.metrics.record_tick(&result);
200 *total_ticks += 1;
201 *total_cycles += tick_cycles;
202
203 if self.config.enable_tracing {
205 self.traces.push_back(AgentTrace {
206 agent_name: slot.agent.name(),
207 timestamp_ns: tick_start_ns,
208 result,
209 });
210
211 if self.traces.len() > 100000 {
213 self.traces.pop_front();
214 }
215 }
216
217 while now_ns() - tick_start_ns < slot.budget_ns {
219 spin();
220 }
221 }
222 }
223
224 fn execute_priority(
226 &mut self,
227 mut now_ns: impl FnMut() -> u128,
228 total_ticks: &mut u64,
229 total_cycles: &mut u64,
230 budget_violations: &mut u64,
231 ) {
232 self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
234 }
235
236 fn execute_hierarchical(
238 &mut self,
239 mut now_ns: impl FnMut() -> u128,
240 total_ticks: &mut u64,
241 total_cycles: &mut u64,
242 budget_violations: &mut u64,
243 ) {
244 self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
247 }
248
249 fn execute_mesh(
251 &mut self,
252 mut now_ns: impl FnMut() -> u128,
253 total_ticks: &mut u64,
254 total_cycles: &mut u64,
255 budget_violations: &mut u64,
256 ) {
257 self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
260 }
261
262 fn execute_quantum(
264 &mut self,
265 mut now_ns: impl FnMut() -> u128,
266 total_ticks: &mut u64,
267 total_cycles: &mut u64,
268 budget_violations: &mut u64,
269 ) {
270 self.execute_round_robin(now_ns, total_ticks, total_cycles, budget_violations);
273 }
274}
275
276#[derive(Debug)]
278pub struct SchedulerStats {
279 pub total_ticks: u64,
280 pub total_cycles: u64,
281 pub budget_violations: u64,
282 pub runtime_ns: u128,
283 pub agent_count: usize,
284 pub traces: Vec<AgentTrace>,
285}
286
287impl SchedulerStats {
288 pub fn avg_ns_per_tick(&self) -> f64 {
290 if self.total_ticks == 0 {
291 0.0
292 } else {
293 self.runtime_ns as f64 / self.total_ticks as f64
294 }
295 }
296
297 pub fn avg_cycles_per_tick(&self) -> f64 {
299 if self.total_ticks == 0 {
300 0.0
301 } else {
302 self.total_cycles as f64 / self.total_ticks as f64
303 }
304 }
305
306 pub fn violation_rate(&self) -> f64 {
308 if self.total_ticks == 0 {
309 0.0
310 } else {
311 self.budget_violations as f64 / self.total_ticks as f64
312 }
313 }
314}