solverforge_solver/stats/
telemetry.rs1use std::time::Duration;
7
8#[derive(Debug, Clone, Default, PartialEq)]
33pub struct SelectorTelemetry {
34 pub selector_index: usize,
35 pub selector_label: String,
36 pub moves_generated: u64,
37 pub moves_evaluated: u64,
38 pub moves_accepted: u64,
39 pub moves_applied: u64,
40 pub moves_not_doable: u64,
41 pub moves_acceptor_rejected: u64,
42 pub moves_forager_ignored: u64,
43 pub moves_hard_improving: u64,
44 pub moves_hard_neutral: u64,
45 pub moves_hard_worse: u64,
46 pub conflict_repair_provider_generated: u64,
47 pub conflict_repair_duplicate_filtered: u64,
48 pub conflict_repair_illegal_filtered: u64,
49 pub conflict_repair_not_doable_filtered: u64,
50 pub conflict_repair_hard_improving: u64,
51 pub conflict_repair_exposed: u64,
52 pub generation_time: Duration,
53 pub evaluation_time: Duration,
54}
55
56#[derive(Debug, Clone, Default, PartialEq)]
57pub struct SolverTelemetry {
58 pub elapsed: Duration,
59 pub step_count: u64,
60 pub moves_generated: u64,
61 pub moves_evaluated: u64,
62 pub moves_accepted: u64,
63 pub moves_applied: u64,
64 pub moves_not_doable: u64,
65 pub moves_acceptor_rejected: u64,
66 pub moves_forager_ignored: u64,
67 pub moves_hard_improving: u64,
68 pub moves_hard_neutral: u64,
69 pub moves_hard_worse: u64,
70 pub conflict_repair_provider_generated: u64,
71 pub conflict_repair_duplicate_filtered: u64,
72 pub conflict_repair_illegal_filtered: u64,
73 pub conflict_repair_not_doable_filtered: u64,
74 pub conflict_repair_hard_improving: u64,
75 pub conflict_repair_exposed: u64,
76 pub score_calculations: u64,
77 pub construction_slots_assigned: u64,
78 pub construction_slots_kept: u64,
79 pub construction_slots_no_doable: u64,
80 pub generation_time: Duration,
81 pub evaluation_time: Duration,
82 pub selector_telemetry: Vec<SelectorTelemetry>,
83}
84
85impl SolverTelemetry {
86 pub const fn new_const() -> Self {
87 Self {
88 elapsed: Duration::ZERO,
89 step_count: 0,
90 moves_generated: 0,
91 moves_evaluated: 0,
92 moves_accepted: 0,
93 moves_applied: 0,
94 moves_not_doable: 0,
95 moves_acceptor_rejected: 0,
96 moves_forager_ignored: 0,
97 moves_hard_improving: 0,
98 moves_hard_neutral: 0,
99 moves_hard_worse: 0,
100 conflict_repair_provider_generated: 0,
101 conflict_repair_duplicate_filtered: 0,
102 conflict_repair_illegal_filtered: 0,
103 conflict_repair_not_doable_filtered: 0,
104 conflict_repair_hard_improving: 0,
105 conflict_repair_exposed: 0,
106 score_calculations: 0,
107 construction_slots_assigned: 0,
108 construction_slots_kept: 0,
109 construction_slots_no_doable: 0,
110 generation_time: Duration::ZERO,
111 evaluation_time: Duration::ZERO,
112 selector_telemetry: Vec::new(),
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
118pub struct Throughput {
119 pub count: u64,
120 pub elapsed: Duration,
121}
122
123pub(crate) fn whole_units_per_second(count: u64, elapsed: Duration) -> u128 {
124 let nanos = elapsed.as_nanos();
125 if nanos == 0 {
126 0
127 } else {
128 u128::from(count)
129 .saturating_mul(1_000_000_000)
130 .checked_div(nanos)
131 .unwrap_or(0)
132 }
133}
134
135pub(crate) fn format_duration(duration: Duration) -> String {
136 let secs = duration.as_secs();
137 let nanos = duration.subsec_nanos();
138
139 if secs >= 60 {
140 let mins = secs / 60;
141 let rem_secs = secs % 60;
142 return format!("{mins}m {rem_secs}s");
143 }
144
145 if secs > 0 {
146 let millis = nanos / 1_000_000;
147 if millis == 0 {
148 return format!("{secs}s");
149 }
150 return format!("{secs}s {millis}ms");
151 }
152
153 let millis = nanos / 1_000_000;
154 if millis > 0 {
155 return format!("{millis}ms");
156 }
157
158 let micros = nanos / 1_000;
159 if micros > 0 {
160 return format!("{micros}us");
161 }
162
163 format!("{nanos}ns")
164}