1use std::collections::HashMap;
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
27pub enum KernelPrecision {
28 F16,
30 F32,
32 F64,
34 I8,
36 I32,
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46pub enum KernelTarget {
47 Cpu,
49 Gpu,
51 Simd,
53 Generic,
55}
56
57#[derive(Clone, Debug)]
63pub struct KernelDescriptor {
64 pub kernel_id: u64,
66 pub name: String,
68 pub version: u32,
70 pub precision: KernelPrecision,
72 pub target: KernelTarget,
74 pub flops_per_element: f64,
76 pub tags: Vec<String>,
78}
79
80#[derive(Clone, Debug, Default)]
88pub struct KernelQuery {
89 pub name: Option<String>,
91 pub precision: Option<KernelPrecision>,
93 pub target: Option<KernelTarget>,
95 pub tag: Option<String>,
97}
98
99#[derive(Clone, Debug, Default)]
105pub struct KernelRegistryStats {
106 pub total_kernels: usize,
108 pub by_target: HashMap<KernelTarget, usize>,
110 pub by_precision: HashMap<KernelPrecision, usize>,
112 pub total_lookups: u64,
115}
116
117pub struct TensorKernelRegistry {
123 kernels: HashMap<u64, KernelDescriptor>,
124 next_id: u64,
125 stats: KernelRegistryStats,
126}
127
128impl Default for TensorKernelRegistry {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl TensorKernelRegistry {
135 pub fn new() -> Self {
137 Self {
138 kernels: HashMap::new(),
139 next_id: 1,
140 stats: KernelRegistryStats::default(),
141 }
142 }
143
144 fn rebuild_histograms(&mut self) {
152 let mut by_target: HashMap<KernelTarget, usize> = HashMap::new();
153 let mut by_precision: HashMap<KernelPrecision, usize> = HashMap::new();
154 for desc in self.kernels.values() {
155 *by_target.entry(desc.target).or_insert(0) += 1;
156 *by_precision.entry(desc.precision).or_insert(0) += 1;
157 }
158 self.stats.by_target = by_target;
159 self.stats.by_precision = by_precision;
160 self.stats.total_kernels = self.kernels.len();
161 }
162
163 pub fn register(
178 &mut self,
179 name: String,
180 version: u32,
181 precision: KernelPrecision,
182 target: KernelTarget,
183 flops_per_element: f64,
184 tags: Vec<String>,
185 ) -> u64 {
186 let kernel_id = self.next_id;
187 self.next_id += 1;
188
189 let descriptor = KernelDescriptor {
190 kernel_id,
191 name,
192 version,
193 precision,
194 target,
195 flops_per_element,
196 tags,
197 };
198 self.kernels.insert(kernel_id, descriptor);
199 self.rebuild_histograms();
200 kernel_id
201 }
202
203 pub fn lookup(&mut self, query: &KernelQuery) -> Vec<&KernelDescriptor> {
210 self.stats.total_lookups += 1;
211
212 let mut matches: Vec<&KernelDescriptor> = self
213 .kernels
214 .values()
215 .filter(|desc| {
216 if let Some(ref name_filter) = query.name {
218 let lc_name = desc.name.to_lowercase();
219 let lc_filter = name_filter.to_lowercase();
220 if !lc_name.contains(lc_filter.as_str()) {
221 return false;
222 }
223 }
224 if let Some(prec) = query.precision {
226 if desc.precision != prec {
227 return false;
228 }
229 }
230 if let Some(tgt) = query.target {
232 if desc.target != tgt {
233 return false;
234 }
235 }
236 if let Some(ref tag_filter) = query.tag {
238 if !desc.tags.iter().any(|t| t == tag_filter) {
239 return false;
240 }
241 }
242 true
243 })
244 .collect();
245
246 matches.sort_by(|a, b| {
248 a.name
249 .cmp(&b.name)
250 .then_with(|| b.version.cmp(&a.version))
251 .then_with(|| a.kernel_id.cmp(&b.kernel_id))
252 });
253
254 matches
255 }
256
257 pub fn best_for(
264 &mut self,
265 name: &str,
266 precision: KernelPrecision,
267 ) -> Option<&KernelDescriptor> {
268 self.stats.total_lookups += 1;
269
270 let candidate_ids: Vec<u64> = self
272 .kernels
273 .values()
274 .filter(|desc| desc.name.eq_ignore_ascii_case(name) && desc.precision == precision)
275 .map(|desc| desc.kernel_id)
276 .collect();
277
278 if candidate_ids.is_empty() {
279 return None;
280 }
281
282 let target_priority = |t: KernelTarget| match t {
284 KernelTarget::Simd => 0u8,
285 KernelTarget::Cpu => 1,
286 KernelTarget::Gpu => 2,
287 KernelTarget::Generic => 3,
288 };
289
290 let best_id = candidate_ids.into_iter().min_by(|&a_id, &b_id| {
292 let a = &self.kernels[&a_id];
293 let b = &self.kernels[&b_id];
294 target_priority(a.target)
295 .cmp(&target_priority(b.target))
296 .then_with(|| b.version.cmp(&a.version)) });
298
299 best_id.and_then(|id| self.kernels.get(&id))
300 }
301
302 pub fn remove(&mut self, kernel_id: u64) -> bool {
306 let removed = self.kernels.remove(&kernel_id).is_some();
307 if removed {
308 self.rebuild_histograms();
309 }
310 removed
311 }
312
313 pub fn get(&self, kernel_id: u64) -> Option<&KernelDescriptor> {
315 self.kernels.get(&kernel_id)
316 }
317
318 pub fn stats(&self) -> &KernelRegistryStats {
320 &self.stats
321 }
322}
323
324#[cfg(test)]
329mod tests {
330 use super::*;
331
332 fn reg_simple(
334 r: &mut TensorKernelRegistry,
335 name: &str,
336 version: u32,
337 precision: KernelPrecision,
338 target: KernelTarget,
339 ) -> u64 {
340 r.register(name.to_string(), version, precision, target, 1.0, vec![])
341 }
342
343 fn reg_tagged(
345 r: &mut TensorKernelRegistry,
346 name: &str,
347 version: u32,
348 precision: KernelPrecision,
349 target: KernelTarget,
350 tags: Vec<&str>,
351 ) -> u64 {
352 r.register(
353 name.to_string(),
354 version,
355 precision,
356 target,
357 1.0,
358 tags.into_iter().map(String::from).collect(),
359 )
360 }
361
362 #[test]
367 fn test_register_creates_kernel() {
368 let mut r = TensorKernelRegistry::new();
369 let id = reg_simple(&mut r, "matmul", 1, KernelPrecision::F32, KernelTarget::Cpu);
370 assert!(r.get(id).is_some());
371 }
372
373 #[test]
374 fn test_register_assigns_unique_ids() {
375 let mut r = TensorKernelRegistry::new();
376 let id1 = reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
377 let id2 = reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Gpu);
378 assert_ne!(id1, id2);
379 }
380
381 #[test]
382 fn test_register_increments_total_kernels() {
383 let mut r = TensorKernelRegistry::new();
384 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
385 reg_simple(
386 &mut r,
387 "sigmoid",
388 1,
389 KernelPrecision::F64,
390 KernelTarget::Gpu,
391 );
392 assert_eq!(r.stats().total_kernels, 2);
393 }
394
395 #[test]
396 fn test_register_stores_correct_metadata() {
397 let mut r = TensorKernelRegistry::new();
398 let id = r.register(
399 "softmax".to_string(),
400 3,
401 KernelPrecision::F16,
402 KernelTarget::Simd,
403 4.0,
404 vec!["fused".to_string(), "stable".to_string()],
405 );
406 let desc = r.get(id).expect("kernel must exist");
407 assert_eq!(desc.name, "softmax");
408 assert_eq!(desc.version, 3);
409 assert_eq!(desc.precision, KernelPrecision::F16);
410 assert_eq!(desc.target, KernelTarget::Simd);
411 assert!((desc.flops_per_element - 4.0).abs() < f64::EPSILON);
412 assert_eq!(desc.tags, vec!["fused", "stable"]);
413 }
414
415 #[test]
420 fn test_lookup_by_name_substring() {
421 let mut r = TensorKernelRegistry::new();
422 reg_simple(&mut r, "matmul", 1, KernelPrecision::F32, KernelTarget::Cpu);
423 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
424 let q = KernelQuery {
425 name: Some("mat".to_string()),
426 ..Default::default()
427 };
428 let results = r.lookup(&q);
429 assert_eq!(results.len(), 1);
430 assert_eq!(results[0].name, "matmul");
431 }
432
433 #[test]
434 fn test_lookup_by_name_case_insensitive() {
435 let mut r = TensorKernelRegistry::new();
436 reg_simple(&mut r, "MatMul", 1, KernelPrecision::F32, KernelTarget::Cpu);
437 let q = KernelQuery {
438 name: Some("matmul".to_string()),
439 ..Default::default()
440 };
441 let results = r.lookup(&q);
442 assert_eq!(results.len(), 1);
443 }
444
445 #[test]
446 fn test_lookup_by_precision() {
447 let mut r = TensorKernelRegistry::new();
448 reg_simple(&mut r, "conv", 1, KernelPrecision::F16, KernelTarget::Cpu);
449 reg_simple(&mut r, "conv", 1, KernelPrecision::F32, KernelTarget::Cpu);
450 reg_simple(&mut r, "conv", 1, KernelPrecision::F64, KernelTarget::Cpu);
451 let q = KernelQuery {
452 precision: Some(KernelPrecision::F32),
453 ..Default::default()
454 };
455 let results = r.lookup(&q);
456 assert_eq!(results.len(), 1);
457 assert_eq!(results[0].precision, KernelPrecision::F32);
458 }
459
460 #[test]
461 fn test_lookup_by_target_simd() {
462 let mut r = TensorKernelRegistry::new();
463 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
464 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Simd);
465 let q = KernelQuery {
466 target: Some(KernelTarget::Simd),
467 ..Default::default()
468 };
469 let results = r.lookup(&q);
470 assert_eq!(results.len(), 1);
471 assert_eq!(results[0].target, KernelTarget::Simd);
472 }
473
474 #[test]
475 fn test_lookup_by_target_gpu() {
476 let mut r = TensorKernelRegistry::new();
477 reg_simple(&mut r, "gemm", 1, KernelPrecision::F32, KernelTarget::Gpu);
478 reg_simple(&mut r, "gemm", 1, KernelPrecision::F32, KernelTarget::Cpu);
479 let q = KernelQuery {
480 target: Some(KernelTarget::Gpu),
481 ..Default::default()
482 };
483 let results = r.lookup(&q);
484 assert_eq!(results.len(), 1);
485 assert_eq!(results[0].target, KernelTarget::Gpu);
486 }
487
488 #[test]
489 fn test_lookup_by_tag_exact_match() {
490 let mut r = TensorKernelRegistry::new();
491 reg_tagged(
492 &mut r,
493 "softmax",
494 1,
495 KernelPrecision::F32,
496 KernelTarget::Cpu,
497 vec!["stable", "blas"],
498 );
499 reg_tagged(
500 &mut r,
501 "relu",
502 1,
503 KernelPrecision::F32,
504 KernelTarget::Cpu,
505 vec!["experimental"],
506 );
507 let q = KernelQuery {
508 tag: Some("stable".to_string()),
509 ..Default::default()
510 };
511 let results = r.lookup(&q);
512 assert_eq!(results.len(), 1);
513 assert_eq!(results[0].name, "softmax");
514 }
515
516 #[test]
517 fn test_lookup_tag_no_partial_match() {
518 let mut r = TensorKernelRegistry::new();
519 reg_tagged(
520 &mut r,
521 "relu",
522 1,
523 KernelPrecision::F32,
524 KernelTarget::Cpu,
525 vec!["stable"],
526 );
527 let q = KernelQuery {
528 tag: Some("stab".to_string()), ..Default::default()
530 };
531 let results = r.lookup(&q);
532 assert!(results.is_empty());
533 }
534
535 #[test]
540 fn test_lookup_combined_name_and_precision() {
541 let mut r = TensorKernelRegistry::new();
542 reg_simple(&mut r, "matmul", 1, KernelPrecision::F32, KernelTarget::Cpu);
543 reg_simple(&mut r, "matmul", 1, KernelPrecision::F64, KernelTarget::Cpu);
544 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
545 let q = KernelQuery {
546 name: Some("matmul".to_string()),
547 precision: Some(KernelPrecision::F32),
548 ..Default::default()
549 };
550 let results = r.lookup(&q);
551 assert_eq!(results.len(), 1);
552 assert_eq!(results[0].name, "matmul");
553 assert_eq!(results[0].precision, KernelPrecision::F32);
554 }
555
556 #[test]
557 fn test_lookup_combined_precision_and_target() {
558 let mut r = TensorKernelRegistry::new();
559 reg_simple(&mut r, "conv", 1, KernelPrecision::F32, KernelTarget::Simd);
560 reg_simple(&mut r, "conv", 1, KernelPrecision::F32, KernelTarget::Gpu);
561 reg_simple(&mut r, "conv", 1, KernelPrecision::F64, KernelTarget::Simd);
562 let q = KernelQuery {
563 precision: Some(KernelPrecision::F32),
564 target: Some(KernelTarget::Simd),
565 ..Default::default()
566 };
567 let results = r.lookup(&q);
568 assert_eq!(results.len(), 1);
569 assert_eq!(results[0].target, KernelTarget::Simd);
570 assert_eq!(results[0].precision, KernelPrecision::F32);
571 }
572
573 #[test]
574 fn test_lookup_all_filters() {
575 let mut r = TensorKernelRegistry::new();
576 reg_tagged(
577 &mut r,
578 "matmul",
579 2,
580 KernelPrecision::F32,
581 KernelTarget::Simd,
582 vec!["fast"],
583 );
584 reg_tagged(
585 &mut r,
586 "matmul",
587 2,
588 KernelPrecision::F32,
589 KernelTarget::Cpu,
590 vec!["fast"],
591 );
592 reg_tagged(
593 &mut r,
594 "gemm",
595 2,
596 KernelPrecision::F32,
597 KernelTarget::Simd,
598 vec!["fast"],
599 );
600 let q = KernelQuery {
601 name: Some("matmul".to_string()),
602 precision: Some(KernelPrecision::F32),
603 target: Some(KernelTarget::Simd),
604 tag: Some("fast".to_string()),
605 };
606 let results = r.lookup(&q);
607 assert_eq!(results.len(), 1);
608 assert_eq!(results[0].name, "matmul");
609 assert_eq!(results[0].target, KernelTarget::Simd);
610 }
611
612 #[test]
617 fn test_lookup_sorted_name_asc_version_desc() {
618 let mut r = TensorKernelRegistry::new();
619 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
620 reg_simple(&mut r, "matmul", 2, KernelPrecision::F32, KernelTarget::Cpu);
621 reg_simple(&mut r, "matmul", 1, KernelPrecision::F32, KernelTarget::Cpu);
622 reg_simple(&mut r, "relu", 3, KernelPrecision::F32, KernelTarget::Cpu);
623 let q = KernelQuery::default();
624 let results = r.lookup(&q);
625 assert_eq!(results.len(), 4);
626 assert_eq!(results[0].name, "matmul");
628 assert_eq!(results[1].name, "matmul");
629 assert_eq!(results[0].version, 2);
631 assert_eq!(results[1].version, 1);
632 assert_eq!(results[2].name, "relu");
634 assert_eq!(results[3].name, "relu");
635 assert_eq!(results[2].version, 3);
637 assert_eq!(results[3].version, 1);
638 }
639
640 #[test]
641 fn test_lookup_empty_registry_returns_nothing() {
642 let mut r = TensorKernelRegistry::new();
643 let results = r.lookup(&KernelQuery::default());
644 assert!(results.is_empty());
645 }
646
647 #[test]
648 fn test_lookup_increments_total_lookups() {
649 let mut r = TensorKernelRegistry::new();
650 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
651 assert_eq!(r.stats().total_lookups, 0);
652 r.lookup(&KernelQuery::default());
653 assert_eq!(r.stats().total_lookups, 1);
654 r.lookup(&KernelQuery::default());
655 assert_eq!(r.stats().total_lookups, 2);
656 }
657
658 #[test]
663 fn test_best_for_prefers_simd_over_cpu() {
664 let mut r = TensorKernelRegistry::new();
665 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
666 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Simd);
667 let best = r.best_for("relu", KernelPrecision::F32).expect("must find");
668 assert_eq!(best.target, KernelTarget::Simd);
669 }
670
671 #[test]
672 fn test_best_for_prefers_cpu_over_gpu() {
673 let mut r = TensorKernelRegistry::new();
674 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Gpu);
675 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
676 let best = r.best_for("relu", KernelPrecision::F32).expect("must find");
677 assert_eq!(best.target, KernelTarget::Cpu);
678 }
679
680 #[test]
681 fn test_best_for_prefers_gpu_over_generic() {
682 let mut r = TensorKernelRegistry::new();
683 reg_simple(
684 &mut r,
685 "conv",
686 1,
687 KernelPrecision::F32,
688 KernelTarget::Generic,
689 );
690 reg_simple(&mut r, "conv", 1, KernelPrecision::F32, KernelTarget::Gpu);
691 let best = r.best_for("conv", KernelPrecision::F32).expect("must find");
692 assert_eq!(best.target, KernelTarget::Gpu);
693 }
694
695 #[test]
696 fn test_best_for_prefers_simd_highest_priority() {
697 let mut r = TensorKernelRegistry::new();
698 reg_simple(&mut r, "op", 1, KernelPrecision::F64, KernelTarget::Generic);
699 reg_simple(&mut r, "op", 1, KernelPrecision::F64, KernelTarget::Gpu);
700 reg_simple(&mut r, "op", 1, KernelPrecision::F64, KernelTarget::Cpu);
701 reg_simple(&mut r, "op", 1, KernelPrecision::F64, KernelTarget::Simd);
702 let best = r.best_for("op", KernelPrecision::F64).expect("must find");
703 assert_eq!(best.target, KernelTarget::Simd);
704 }
705
706 #[test]
707 fn test_best_for_prefers_higher_version_same_target() {
708 let mut r = TensorKernelRegistry::new();
709 reg_simple(&mut r, "matmul", 1, KernelPrecision::F32, KernelTarget::Cpu);
710 reg_simple(&mut r, "matmul", 5, KernelPrecision::F32, KernelTarget::Cpu);
711 reg_simple(&mut r, "matmul", 3, KernelPrecision::F32, KernelTarget::Cpu);
712 let best = r
713 .best_for("matmul", KernelPrecision::F32)
714 .expect("must find");
715 assert_eq!(best.version, 5);
716 }
717
718 #[test]
719 fn test_best_for_simd_higher_version_wins_over_simd_lower() {
720 let mut r = TensorKernelRegistry::new();
721 reg_simple(
722 &mut r,
723 "sigmoid",
724 2,
725 KernelPrecision::F32,
726 KernelTarget::Simd,
727 );
728 reg_simple(
729 &mut r,
730 "sigmoid",
731 7,
732 KernelPrecision::F32,
733 KernelTarget::Simd,
734 );
735 let best = r
736 .best_for("sigmoid", KernelPrecision::F32)
737 .expect("must find");
738 assert_eq!(best.version, 7);
739 }
740
741 #[test]
742 fn test_best_for_returns_none_when_no_name_match() {
743 let mut r = TensorKernelRegistry::new();
744 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
745 assert!(r.best_for("matmul", KernelPrecision::F32).is_none());
746 }
747
748 #[test]
749 fn test_best_for_returns_none_when_no_precision_match() {
750 let mut r = TensorKernelRegistry::new();
751 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
752 assert!(r.best_for("relu", KernelPrecision::F64).is_none());
753 }
754
755 #[test]
756 fn test_best_for_empty_registry_returns_none() {
757 let mut r = TensorKernelRegistry::new();
758 assert!(r.best_for("anything", KernelPrecision::F32).is_none());
759 }
760
761 #[test]
762 fn test_best_for_increments_total_lookups() {
763 let mut r = TensorKernelRegistry::new();
764 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
765 assert_eq!(r.stats().total_lookups, 0);
766 let _ = r.best_for("relu", KernelPrecision::F32);
767 assert_eq!(r.stats().total_lookups, 1);
768 }
769
770 #[test]
775 fn test_remove_returns_true_when_exists() {
776 let mut r = TensorKernelRegistry::new();
777 let id = reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
778 assert!(r.remove(id));
779 }
780
781 #[test]
782 fn test_remove_returns_false_when_not_exists() {
783 let mut r = TensorKernelRegistry::new();
784 assert!(!r.remove(9999));
785 }
786
787 #[test]
788 fn test_remove_decrements_total_kernels() {
789 let mut r = TensorKernelRegistry::new();
790 let id = reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
791 assert_eq!(r.stats().total_kernels, 1);
792 r.remove(id);
793 assert_eq!(r.stats().total_kernels, 0);
794 }
795
796 #[test]
797 fn test_remove_makes_kernel_unreachable() {
798 let mut r = TensorKernelRegistry::new();
799 let id = reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
800 r.remove(id);
801 assert!(r.get(id).is_none());
802 }
803
804 #[test]
805 fn test_remove_idempotent_second_call() {
806 let mut r = TensorKernelRegistry::new();
807 let id = reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
808 assert!(r.remove(id));
809 assert!(!r.remove(id));
810 }
811
812 #[test]
817 fn test_get_returns_some_for_existing() {
818 let mut r = TensorKernelRegistry::new();
819 let id = reg_simple(
820 &mut r,
821 "conv",
822 1,
823 KernelPrecision::I8,
824 KernelTarget::Generic,
825 );
826 let desc = r.get(id);
827 assert!(desc.is_some());
828 assert_eq!(desc.expect("test: should succeed").name, "conv");
829 }
830
831 #[test]
832 fn test_get_returns_none_for_missing() {
833 let r = TensorKernelRegistry::new();
834 assert!(r.get(42).is_none());
835 }
836
837 #[test]
842 fn test_stats_by_target() {
843 let mut r = TensorKernelRegistry::new();
844 reg_simple(&mut r, "a", 1, KernelPrecision::F32, KernelTarget::Cpu);
845 reg_simple(&mut r, "b", 1, KernelPrecision::F32, KernelTarget::Cpu);
846 reg_simple(&mut r, "c", 1, KernelPrecision::F32, KernelTarget::Gpu);
847 let stats = r.stats();
848 assert_eq!(stats.by_target[&KernelTarget::Cpu], 2);
849 assert_eq!(stats.by_target[&KernelTarget::Gpu], 1);
850 }
851
852 #[test]
853 fn test_stats_by_precision() {
854 let mut r = TensorKernelRegistry::new();
855 reg_simple(&mut r, "a", 1, KernelPrecision::F32, KernelTarget::Cpu);
856 reg_simple(&mut r, "b", 1, KernelPrecision::F16, KernelTarget::Cpu);
857 reg_simple(&mut r, "c", 1, KernelPrecision::I32, KernelTarget::Cpu);
858 reg_simple(&mut r, "d", 1, KernelPrecision::I32, KernelTarget::Cpu);
859 let stats = r.stats();
860 assert_eq!(stats.by_precision[&KernelPrecision::F32], 1);
861 assert_eq!(stats.by_precision[&KernelPrecision::F16], 1);
862 assert_eq!(stats.by_precision[&KernelPrecision::I32], 2);
863 }
864
865 #[test]
866 fn test_stats_histograms_updated_on_remove() {
867 let mut r = TensorKernelRegistry::new();
868 let id = reg_simple(&mut r, "a", 1, KernelPrecision::F32, KernelTarget::Simd);
869 assert_eq!(r.stats().by_target[&KernelTarget::Simd], 1);
870 r.remove(id);
871 assert_eq!(
872 r.stats()
873 .by_target
874 .get(&KernelTarget::Simd)
875 .copied()
876 .unwrap_or(0),
877 0
878 );
879 }
880
881 #[test]
882 fn test_stats_total_lookups_combined() {
883 let mut r = TensorKernelRegistry::new();
884 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
885 r.lookup(&KernelQuery::default());
886 let _ = r.best_for("relu", KernelPrecision::F32);
887 r.lookup(&KernelQuery::default());
888 assert_eq!(r.stats().total_lookups, 3);
889 }
890
891 #[test]
892 fn test_lookup_i8_precision() {
893 let mut r = TensorKernelRegistry::new();
894 reg_simple(
895 &mut r,
896 "quantized_matmul",
897 1,
898 KernelPrecision::I8,
899 KernelTarget::Cpu,
900 );
901 reg_simple(&mut r, "relu", 1, KernelPrecision::F32, KernelTarget::Cpu);
902 let q = KernelQuery {
903 precision: Some(KernelPrecision::I8),
904 ..Default::default()
905 };
906 let results = r.lookup(&q);
907 assert_eq!(results.len(), 1);
908 assert_eq!(results[0].name, "quantized_matmul");
909 }
910}