1use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8use std::time::{Duration, Instant};
9
10#[derive(Debug, Clone)]
12pub struct ArmFeatures {
13 pub has_neon: bool,
15 pub is_aarch64: bool,
17 pub is_armv7: bool,
19}
20
21impl ArmFeatures {
22 pub fn detect() -> Self {
24 let is_aarch64 = cfg!(target_arch = "aarch64");
25 let is_armv7 = cfg!(target_arch = "arm");
26
27 let has_neon = if is_aarch64 {
29 true
30 } else if is_armv7 {
31 #[cfg(target_arch = "arm")]
33 {
34 std::arch::is_arm_feature_detected!("neon")
37 }
38 #[cfg(not(target_arch = "arm"))]
39 {
40 false
41 }
42 } else {
43 false
44 };
45
46 Self {
47 has_neon,
48 is_aarch64,
49 is_armv7,
50 }
51 }
52
53 pub fn is_arm(&self) -> bool {
55 self.is_aarch64 || self.is_armv7
56 }
57}
58
59#[derive(Debug, Clone)]
61pub struct ArmPerfCounter {
62 name: String,
63 count: Arc<AtomicU64>,
64 total_time_ns: Arc<AtomicU64>,
65}
66
67impl ArmPerfCounter {
68 pub fn new(name: impl Into<String>) -> Self {
70 Self {
71 name: name.into(),
72 count: Arc::new(AtomicU64::new(0)),
73 total_time_ns: Arc::new(AtomicU64::new(0)),
74 }
75 }
76
77 pub fn start(&self) -> ArmPerfTimer {
79 ArmPerfTimer {
80 counter: self.clone(),
81 start: Instant::now(),
82 }
83 }
84
85 pub fn count(&self) -> u64 {
87 self.count.load(Ordering::Relaxed)
88 }
89
90 pub fn total_time(&self) -> Duration {
92 Duration::from_nanos(self.total_time_ns.load(Ordering::Relaxed))
93 }
94
95 pub fn avg_time(&self) -> Duration {
97 let count = self.count();
98 Duration::from_nanos(
99 self.total_time_ns
100 .load(Ordering::Relaxed)
101 .checked_div(count)
102 .unwrap_or(0),
103 )
104 }
105
106 pub fn reset(&self) {
108 self.count.store(0, Ordering::Relaxed);
109 self.total_time_ns.store(0, Ordering::Relaxed);
110 }
111
112 pub fn name(&self) -> &str {
114 &self.name
115 }
116}
117
118pub struct ArmPerfTimer {
120 counter: ArmPerfCounter,
121 start: Instant,
122}
123
124impl Drop for ArmPerfTimer {
125 fn drop(&mut self) {
126 let elapsed = self.start.elapsed().as_nanos() as u64;
127 self.counter.count.fetch_add(1, Ordering::Relaxed);
128 self.counter
129 .total_time_ns
130 .fetch_add(elapsed, Ordering::Relaxed);
131 }
132}
133
134#[derive(Debug, Clone)]
136pub struct ArmPerfReport {
137 pub features: ArmFeatures,
139 pub counters: Vec<(String, u64, Duration, Duration)>, }
142
143impl ArmPerfReport {
144 pub fn from_counters(counters: &[ArmPerfCounter]) -> Self {
146 let features = ArmFeatures::detect();
147 let counters = counters
148 .iter()
149 .map(|c| {
150 (
151 c.name().to_string(),
152 c.count(),
153 c.total_time(),
154 c.avg_time(),
155 )
156 })
157 .collect();
158
159 Self { features, counters }
160 }
161
162 pub fn print(&self) {
164 println!("=== ARM Performance Report ===");
165 println!(
166 "Architecture: {}",
167 if self.features.is_aarch64 {
168 "AArch64"
169 } else if self.features.is_armv7 {
170 "ARMv7"
171 } else {
172 "x86_64 (not ARM)"
173 }
174 );
175 println!("NEON support: {}", self.features.has_neon);
176 println!("\nPerformance Counters:");
177
178 for (name, count, total, avg) in &self.counters {
179 println!(" {name}: {count} ops, total: {total:?}, avg: {avg:?}");
180 }
181 }
182}
183
184#[cfg(target_arch = "aarch64")]
186pub mod neon_hash {
187 use std::arch::aarch64::*;
188
189 #[target_feature(enable = "neon")]
199 pub unsafe fn hash_block_neon(data: &[u8]) -> u64 {
200 let mut hash = 0xcbf29ce484222325u64; const FNV_PRIME: u64 = 0x100000001b3;
202
203 let chunks = data.chunks_exact(16);
205 let remainder = chunks.remainder();
206
207 for chunk in chunks {
208 let v = vld1q_u8(chunk.as_ptr());
210
211 let bytes: [u8; 16] = std::mem::transmute(v);
215 for &byte in &bytes {
216 hash ^= byte as u64;
217 hash = hash.wrapping_mul(FNV_PRIME);
218 }
219 }
220
221 for &byte in remainder {
223 hash ^= byte as u64;
224 hash = hash.wrapping_mul(FNV_PRIME);
225 }
226
227 hash
228 }
229}
230
231pub fn hash_block_fallback(data: &[u8]) -> u64 {
233 let mut hash = 0xcbf29ce484222325u64; const FNV_PRIME: u64 = 0x100000001b3;
235
236 for &byte in data {
237 hash ^= byte as u64;
238 hash = hash.wrapping_mul(FNV_PRIME);
239 }
240
241 hash
242}
243
244pub fn hash_block(data: &[u8]) -> u64 {
246 #[cfg(target_arch = "aarch64")]
247 {
248 unsafe { neon_hash::hash_block_neon(data) }
250 }
251
252 #[cfg(not(target_arch = "aarch64"))]
253 {
254 hash_block_fallback(data)
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261pub enum PowerProfile {
262 Performance,
264 Balanced,
266 LowPower,
268 Custom {
270 batch_size: usize,
271 batch_delay_ms: u64,
272 },
273}
274
275impl PowerProfile {
276 pub fn batch_size(&self) -> usize {
278 match self {
279 PowerProfile::Performance => 1,
280 PowerProfile::Balanced => 10,
281 PowerProfile::LowPower => 50,
282 PowerProfile::Custom { batch_size, .. } => *batch_size,
283 }
284 }
285
286 pub fn batch_delay_ms(&self) -> u64 {
288 match self {
289 PowerProfile::Performance => 0,
290 PowerProfile::Balanced => 10,
291 PowerProfile::LowPower => 100,
292 PowerProfile::Custom { batch_delay_ms, .. } => *batch_delay_ms,
293 }
294 }
295
296 pub fn batch_delay(&self) -> Duration {
298 Duration::from_millis(self.batch_delay_ms())
299 }
300}
301
302pub struct LowPowerBatcher<T> {
307 profile: PowerProfile,
308 buffer: Arc<std::sync::Mutex<Vec<T>>>,
309}
310
311impl<T> LowPowerBatcher<T> {
312 pub fn new(profile: PowerProfile) -> Self {
314 Self {
315 profile,
316 buffer: Arc::new(std::sync::Mutex::new(Vec::new())),
317 }
318 }
319
320 pub fn push(&self, item: T) -> Option<Vec<T>> {
324 let mut buffer = self.buffer.lock().unwrap_or_else(|e| e.into_inner());
325 buffer.push(item);
326
327 if buffer.len() >= self.profile.batch_size() {
328 Some(std::mem::take(&mut *buffer))
329 } else {
330 None
331 }
332 }
333
334 pub fn flush(&self) -> Vec<T> {
336 let mut buffer = self.buffer.lock().unwrap_or_else(|e| e.into_inner());
337 std::mem::take(&mut *buffer)
338 }
339
340 pub fn profile(&self) -> PowerProfile {
342 self.profile
343 }
344
345 pub fn pending(&self) -> usize {
347 self.buffer.lock().unwrap_or_else(|e| e.into_inner()).len()
348 }
349}
350
351#[derive(Debug, Clone, Default)]
353pub struct PowerStats {
354 pub wakeups: u64,
356 pub operations: u64,
358 pub delay_time: Duration,
360}
361
362impl PowerStats {
363 pub fn new() -> Self {
365 Self::default()
366 }
367
368 pub fn record_batch(&mut self, ops: usize, delay: Duration) {
370 self.wakeups += 1;
371 self.operations += ops as u64;
372 self.delay_time += delay;
373 }
374
375 pub fn avg_ops_per_wakeup(&self) -> f64 {
377 if self.wakeups == 0 {
378 0.0
379 } else {
380 self.operations as f64 / self.wakeups as f64
381 }
382 }
383
384 pub fn power_saving_ratio(&self) -> f64 {
389 if self.operations == 0 {
390 1.0
391 } else {
392 self.wakeups as f64 / self.operations as f64
393 }
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn test_arm_features() {
403 let _features = ArmFeatures::detect();
404
405 #[cfg(target_arch = "aarch64")]
407 {
408 assert!(_features.is_aarch64);
409 assert!(_features.has_neon);
410 }
411
412 #[cfg(target_arch = "arm")]
413 {
414 assert!(_features.is_armv7);
415 }
416
417 #[cfg(not(any(target_arch = "aarch64", target_arch = "arm")))]
419 {
420 assert!(!_features.is_arm());
421 }
422 }
423
424 #[test]
425 fn test_perf_counter() {
426 let counter = ArmPerfCounter::new("test_op");
427
428 {
429 let _timer = counter.start();
430 std::thread::sleep(Duration::from_millis(10));
431 }
432
433 assert_eq!(counter.count(), 1);
434 assert!(counter.total_time() >= Duration::from_millis(10));
435 assert!(counter.avg_time() >= Duration::from_millis(10));
436 }
437
438 #[test]
439 fn test_hash_block() {
440 let data = b"hello world";
441 let hash1 = hash_block(data);
442
443 #[cfg(not(target_arch = "aarch64"))]
445 {
446 let hash2 = hash_block_fallback(data);
447 assert_eq!(hash1, hash2);
448 }
449
450 assert_eq!(hash1, hash_block(data));
452 }
453
454 #[test]
455 fn test_perf_report() {
456 let counter1 = ArmPerfCounter::new("op1");
457 let counter2 = ArmPerfCounter::new("op2");
458
459 {
460 let _t = counter1.start();
461 std::thread::sleep(Duration::from_millis(1));
462 }
463
464 {
465 let _t = counter2.start();
466 std::thread::sleep(Duration::from_millis(1));
467 }
468
469 let report = ArmPerfReport::from_counters(&[counter1, counter2]);
470 assert_eq!(report.counters.len(), 2);
471 }
472
473 #[test]
474 fn test_power_profile() {
475 let perf = PowerProfile::Performance;
476 assert_eq!(perf.batch_size(), 1);
477 assert_eq!(perf.batch_delay_ms(), 0);
478
479 let balanced = PowerProfile::Balanced;
480 assert_eq!(balanced.batch_size(), 10);
481 assert_eq!(balanced.batch_delay_ms(), 10);
482
483 let low = PowerProfile::LowPower;
484 assert_eq!(low.batch_size(), 50);
485 assert_eq!(low.batch_delay_ms(), 100);
486
487 let custom = PowerProfile::Custom {
488 batch_size: 20,
489 batch_delay_ms: 30,
490 };
491 assert_eq!(custom.batch_size(), 20);
492 assert_eq!(custom.batch_delay_ms(), 30);
493 }
494
495 #[test]
496 fn test_low_power_batcher() {
497 let batcher: LowPowerBatcher<i32> = LowPowerBatcher::new(PowerProfile::Custom {
498 batch_size: 3,
499 batch_delay_ms: 0,
500 });
501
502 assert_eq!(batcher.pending(), 0);
503
504 assert!(batcher.push(1).is_none());
506 assert_eq!(batcher.pending(), 1);
507
508 assert!(batcher.push(2).is_none());
509 assert_eq!(batcher.pending(), 2);
510
511 let batch = batcher.push(3);
513 assert!(batch.is_some());
514 let batch = batch.unwrap();
515 assert_eq!(batch, vec![1, 2, 3]);
516 assert_eq!(batcher.pending(), 0);
517
518 batcher.push(4);
520 batcher.push(5);
521 let flushed = batcher.flush();
522 assert_eq!(flushed, vec![4, 5]);
523 assert_eq!(batcher.pending(), 0);
524 }
525
526 #[test]
527 fn test_power_stats() {
528 let mut stats = PowerStats::new();
529 assert_eq!(stats.wakeups, 0);
530 assert_eq!(stats.operations, 0);
531
532 stats.record_batch(10, Duration::from_millis(5));
533 assert_eq!(stats.wakeups, 1);
534 assert_eq!(stats.operations, 10);
535 assert_eq!(stats.avg_ops_per_wakeup(), 10.0);
536
537 stats.record_batch(5, Duration::from_millis(5));
538 assert_eq!(stats.wakeups, 2);
539 assert_eq!(stats.operations, 15);
540 assert_eq!(stats.avg_ops_per_wakeup(), 7.5);
541
542 let ratio = stats.power_saving_ratio();
544 assert!(ratio > 0.0 && ratio < 1.0);
545 }
546}