1use std::path::{Path, PathBuf};
53
54use crate::qstar::{BandwidthProfile, MoeBackend, QStarPolicy};
55
56pub const PROFILE_PATH_ENV: &str = "FERROX_BENCHBW_PATH";
58
59pub const PROFILE_SUBDIR: &str = "benchbw";
61
62pub const LEGACY_PROFILE_FILE: &str = "benchbw.json";
64
65pub fn bench_format(quant_format: &str) -> &str {
81 match quant_format {
82 "nvfp4" => "nvfp4",
83 "ds_fp4" => "ds_fp4",
84 "mxfp4" => "mxfp4_triton",
85 "bf16" => "bf16",
86 "fp8_block" => "fp8_block",
87 other => other,
88 }
89}
90
91pub fn cache_dir() -> PathBuf {
101 std::env::var("XDG_CACHE_HOME")
102 .ok()
103 .filter(|s| !s.is_empty())
104 .map(PathBuf::from)
105 .or_else(|| {
106 std::env::var("HOME")
107 .ok()
108 .filter(|s| !s.is_empty())
109 .map(|h| PathBuf::from(h).join(".cache"))
110 })
111 .unwrap_or_else(std::env::temp_dir)
112 .join("ferrox")
113}
114
115pub fn env_profile_path() -> Option<PathBuf> {
121 std::env::var(PROFILE_PATH_ENV)
122 .ok()
123 .filter(|s| !s.is_empty())
124 .map(PathBuf::from)
125}
126
127pub fn default_profile_path_in(cache_dir: &Path, gpu_uuid: Option<&str>) -> PathBuf {
133 match gpu_uuid.filter(|u| !u.is_empty()) {
134 Some(uuid) => cache_dir.join(PROFILE_SUBDIR).join(format!("{uuid}.json")),
135 None => cache_dir.join(LEGACY_PROFILE_FILE),
136 }
137}
138
139pub fn default_profile_path(gpu_uuid: Option<&str>) -> PathBuf {
141 default_profile_path_in(&cache_dir(), gpu_uuid)
142}
143
144#[derive(Debug, Clone, Copy, Default, PartialEq)]
153pub struct Measured {
154 pub cpu_moe_gbs: Option<f64>,
155 pub pcie_gather_gbs: Option<f64>,
156 pub cpu_moe_overlap_gbs: Option<f64>,
157 pub pcie_gather_overlap_gbs: Option<f64>,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
162pub enum NotMeasurable {
163 OnlyOneSide,
169 NotPositive,
172}
173
174impl std::fmt::Display for NotMeasurable {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 match self {
177 NotMeasurable::OnlyOneSide => write!(
178 f,
179 "only one side was measured; the fetch fraction is a ratio of \
180 the two, so one number alone says nothing about the split"
181 ),
182 NotMeasurable::NotPositive => {
183 write!(
184 f,
185 "a bandwidth came back at or below zero, which is a failed measurement"
186 )
187 }
188 }
189 }
190}
191
192impl std::error::Error for NotMeasurable {}
193
194pub fn entry_from(
203 measured: &Measured,
204 threshold: f64,
205) -> Result<crate::qstar::KernelBandwidths, NotMeasurable> {
206 let positive = |v: Option<f64>| -> Result<Option<f64>, NotMeasurable> {
207 match v {
208 Some(x) if x > 0.0 && x.is_finite() => Ok(Some(x)),
209 Some(_) => Err(NotMeasurable::NotPositive),
210 None => Ok(None),
211 }
212 };
213 let cpu = positive(measured.cpu_moe_gbs)?;
214 let pcie = positive(measured.pcie_gather_gbs)?;
215 let cpu_ov = positive(measured.cpu_moe_overlap_gbs)?;
216 let pcie_ov = positive(measured.pcie_gather_overlap_gbs)?;
217
218 if cpu.is_some() != pcie.is_some() || cpu_ov.is_some() != pcie_ov.is_some() {
219 return Err(NotMeasurable::OnlyOneSide);
220 }
221 let (Some(cpu), Some(pcie)) = (cpu, pcie) else {
222 return Err(NotMeasurable::OnlyOneSide);
223 };
224
225 let (verdict_cpu, verdict_pcie) = match (cpu_ov, pcie_ov) {
229 (Some(c), Some(p)) => (c, p),
230 _ => (cpu, pcie),
231 };
232 Ok(crate::qstar::KernelBandwidths {
233 cpu_moe_gbs: Some(cpu),
234 pcie_gather_gbs: Some(pcie),
235 cpu_moe_overlap_gbs: cpu_ov,
236 pcie_gather_overlap_gbs: pcie_ov,
237 recommended: Some(crate::qstar::recommend_backend(
238 verdict_cpu,
239 verdict_pcie,
240 threshold,
241 )),
242 })
243}
244
245pub fn write_profile(path: &Path, profile: &BandwidthProfile) -> std::io::Result<()> {
252 if let Some(parent) = path.parent() {
253 std::fs::create_dir_all(parent)?;
254 }
255 let body = serde_json::to_vec_pretty(profile)
256 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
257 let tmp = path.with_extension("json.partial");
258 std::fs::write(&tmp, body)?;
259 std::fs::rename(&tmp, path)
260}
261
262pub fn latest_profile_path_in(cache_dir: &Path) -> Option<PathBuf> {
273 let mut found: Vec<(std::time::SystemTime, PathBuf)> = Vec::new();
274 if let Ok(entries) = std::fs::read_dir(cache_dir.join(PROFILE_SUBDIR)) {
275 for entry in entries.flatten() {
276 if !entry.file_name().to_string_lossy().ends_with(".json") {
277 continue;
278 }
279 let Ok(mtime) = entry.metadata().and_then(|m| m.modified()) else {
280 continue;
281 };
282 found.push((mtime, entry.path()));
283 }
284 }
285 if let Some((_, path)) = found.into_iter().max() {
286 return Some(path);
287 }
288 let legacy = default_profile_path_in(cache_dir, None);
289 legacy.is_file().then_some(legacy)
290}
291
292pub fn latest_profile_path() -> Option<PathBuf> {
294 latest_profile_path_in(&cache_dir())
295}
296
297pub fn read_profile(path: &Path) -> Option<BandwidthProfile> {
305 match read_candidate(path) {
306 Candidate::Profile(profile) => Some(*profile),
307 _ => None,
308 }
309}
310
311enum Candidate {
313 Missing,
315 Corrupt,
317 Profile(Box<BandwidthProfile>),
320}
321
322fn read_candidate(path: &Path) -> Candidate {
331 let body = match std::fs::read_to_string(path) {
332 Ok(body) => body,
333 Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Candidate::Missing,
334 Err(_) => return Candidate::Corrupt,
335 };
336 match serde_json::from_str::<BandwidthProfile>(&body) {
337 Ok(profile) => Candidate::Profile(Box::new(profile)),
338 Err(_) => Candidate::Corrupt,
339 }
340}
341
342fn candidate_paths(cache_dir: &Path, path: Option<&Path>, gpu_uuid: Option<&str>) -> Vec<PathBuf> {
348 if let Some(explicit) = path {
349 return vec![explicit.to_path_buf()];
350 }
351 let mut candidates = Vec::with_capacity(2);
352 if gpu_uuid.is_some_and(|u| !u.is_empty()) {
353 candidates.push(default_profile_path_in(cache_dir, gpu_uuid));
354 }
355 candidates.push(default_profile_path_in(cache_dir, None));
356 candidates
357}
358
359pub fn usable_profile_in(
375 cache_dir: &Path,
376 gpu_name: Option<&str>,
377 path: Option<&Path>,
378 gpu_uuid: Option<&str>,
379) -> Option<BandwidthProfile> {
380 let mut found: Option<BandwidthProfile> = None;
381 for candidate in candidate_paths(cache_dir, path, gpu_uuid) {
382 match read_candidate(&candidate) {
383 Candidate::Profile(profile) => {
384 found = Some(*profile);
385 break;
386 }
387 Candidate::Corrupt => return None,
390 Candidate::Missing => continue,
391 }
392 }
393 let profile = found?;
394 profile.matches_gpu(gpu_name).then_some(profile)
395}
396
397pub fn usable_profile(
400 gpu_name: Option<&str>,
401 path: Option<&Path>,
402 gpu_uuid: Option<&str>,
403) -> Option<BandwidthProfile> {
404 let from_env = path.is_none().then(env_profile_path).flatten();
405 let explicit = path.or(from_env.as_deref());
406 usable_profile_in(&cache_dir(), gpu_name, explicit, gpu_uuid)
407}
408
409pub fn load_backend_recommendation(
418 quant_format: &str,
419 gpu_name: Option<&str>,
420 path: Option<&Path>,
421 gpu_uuid: Option<&str>,
422) -> Option<MoeBackend> {
423 usable_profile(gpu_name, path, gpu_uuid)?.backend_for(bench_format(quant_format))
424}
425
426pub fn load_hybrid_fetch_fraction(
432 quant_format: &str,
433 gpu_name: Option<&str>,
434 path: Option<&Path>,
435 gpu_uuid: Option<&str>,
436) -> Option<f64> {
437 usable_profile(gpu_name, path, gpu_uuid)?.fetch_fraction_for(bench_format(quant_format))
438}
439
440pub fn load_policy(
446 quant_format: &str,
447 gpu_name: Option<&str>,
448 path: Option<&Path>,
449 gpu_uuid: Option<&str>,
450) -> QStarPolicy {
451 match usable_profile(gpu_name, path, gpu_uuid) {
452 Some(profile) => profile.policy_for(bench_format(quant_format)),
453 None => QStarPolicy::fixed_cap(1),
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use std::sync::atomic::{AtomicUsize, Ordering};
461 use std::time::{Duration, SystemTime};
462
463 struct TempDir {
467 path: PathBuf,
468 }
469
470 impl TempDir {
471 fn new(tag: &str) -> Self {
472 static COUNTER: AtomicUsize = AtomicUsize::new(0);
473 let n = COUNTER.fetch_add(1, Ordering::Relaxed);
474 let path = std::env::temp_dir().join(format!(
475 "ferrox-edge-bench-profile-{}-{tag}-{n}",
476 std::process::id()
477 ));
478 let _ = std::fs::remove_dir_all(&path);
479 std::fs::create_dir_all(&path).expect("temp dir is creatable");
480 TempDir { path }
481 }
482
483 fn path(&self) -> &Path {
484 &self.path
485 }
486 }
487
488 impl Drop for TempDir {
489 fn drop(&mut self) {
490 let _ = std::fs::remove_dir_all(&self.path);
491 }
492 }
493
494 fn write(path: &Path, body: &str) {
495 if let Some(parent) = path.parent() {
496 std::fs::create_dir_all(parent).expect("parent is creatable");
497 }
498 std::fs::write(path, body).expect("file is writable");
499 }
500
501 fn set_mtime(path: &Path, epoch_secs: u64) {
502 let file = std::fs::File::options()
503 .write(true)
504 .open(path)
505 .expect("file is openable");
506 file.set_modified(SystemTime::UNIX_EPOCH + Duration::from_secs(epoch_secs))
507 .expect("mtime is settable");
508 }
509
510 const CARD_PROFILE: &str = r#"{
513 "version": 4,
514 "gpu": {"name": "NVIDIA GeForce RTX 4090", "uuid": "GPU-slot0"},
515 "dtypes": {"nvfp4": "hybrid", "mxfp4_triton": "hybrid"},
516 "dtype_kernels": {
517 "nvfp4": {"cpu_moe_gbs": 100.0, "pcie_gather_gbs": 40.0,
518 "cpu_moe_overlap_gbs": 90.0, "pcie_gather_overlap_gbs": 30.0},
519 "mxfp4_triton": {"cpu_moe_gbs": 80.0, "pcie_gather_gbs": 50.0}
520 }
521 }"#;
522
523 const LEGACY_PROFILE: &str = r#"{
528 "version": 4,
529 "gpu": {"name": "NVIDIA GeForce RTX 4090", "uuid": "GPU-other"},
530 "dtypes": {"nvfp4": "offload"},
531 "dtype_kernels": {
532 "nvfp4": {"cpu_moe_overlap_gbs": 80.0, "pcie_gather_overlap_gbs": 20.0}
533 }
534 }"#;
535
536 #[test]
537 fn the_quant_name_maps_onto_the_bench_format_key() {
538 assert_eq!(bench_format("mxfp4"), "mxfp4_triton");
539 assert_eq!(bench_format("nvfp4"), "nvfp4");
540 assert_eq!(bench_format("ds_fp4"), "ds_fp4");
541 assert_eq!(bench_format("bf16"), "bf16");
542 assert_eq!(bench_format("fp8_block"), "fp8_block");
543 }
544
545 #[test]
549 fn an_unmapped_quant_name_is_passed_through_and_finds_no_entry() {
550 assert_eq!(bench_format("q4_k_m"), "q4_k_m");
551 let dir = TempDir::new("unmapped");
552 let path = dir.path().join("profile.json");
553 write(&path, CARD_PROFILE);
554 assert_eq!(
555 load_hybrid_fetch_fraction("q4_k_m", None, Some(&path), None),
556 None
557 );
558 assert_eq!(
559 load_backend_recommendation("q4_k_m", None, Some(&path), None),
560 None
561 );
562 assert_eq!(
563 load_policy("q4_k_m", None, Some(&path), None),
564 QStarPolicy::fixed_cap(1)
565 );
566 }
567
568 #[test]
571 fn the_profile_path_is_one_file_per_gpu_uuid() {
572 let root = Path::new("/cache/ferrox");
573 assert_eq!(
574 default_profile_path_in(root, Some("GPU-slot0")),
575 Path::new("/cache/ferrox/benchbw/GPU-slot0.json")
576 );
577 assert_eq!(
578 default_profile_path_in(root, Some("GPU-slot1")),
579 Path::new("/cache/ferrox/benchbw/GPU-slot1.json")
580 );
581 assert_eq!(
582 default_profile_path_in(root, None),
583 Path::new("/cache/ferrox/benchbw.json")
584 );
585 assert_eq!(
586 default_profile_path_in(root, Some("")),
587 Path::new("/cache/ferrox/benchbw.json"),
588 "an empty uuid is no uuid"
589 );
590 }
591
592 #[test]
593 fn the_newest_per_gpu_profile_is_the_latest_one() {
594 let dir = TempDir::new("latest");
595 let older = dir.path().join(PROFILE_SUBDIR).join("GPU-slot0.json");
596 let newer = dir.path().join(PROFILE_SUBDIR).join("GPU-slot1.json");
597 write(&older, CARD_PROFILE);
598 write(&newer, CARD_PROFILE);
599 write(&dir.path().join(LEGACY_PROFILE_FILE), LEGACY_PROFILE);
600 set_mtime(&older, 1_700_000_000);
601 set_mtime(&newer, 1_700_000_100);
602 assert_eq!(latest_profile_path_in(dir.path()), Some(newer.clone()));
603 set_mtime(&older, 1_700_000_200);
605 assert_eq!(latest_profile_path_in(dir.path()), Some(older));
606 }
607
608 #[test]
609 fn the_legacy_file_answers_when_there_is_no_per_gpu_profile() {
610 let dir = TempDir::new("legacy-latest");
611 assert_eq!(
612 latest_profile_path_in(dir.path()),
613 None,
614 "an unbenched host has no profile at all"
615 );
616 let legacy = dir.path().join(LEGACY_PROFILE_FILE);
617 write(&legacy, LEGACY_PROFILE);
618 assert_eq!(latest_profile_path_in(dir.path()), Some(legacy));
619 }
620
621 #[test]
628 fn a_corrupt_profile_for_this_card_is_not_replaced_by_the_legacy_file() {
629 let dir = TempDir::new("corrupt");
630 write(
631 &dir.path().join(PROFILE_SUBDIR).join("GPU-slot0.json"),
632 "{\"dtypes\": {\"nvfp4\": \"hyb",
633 );
634 write(&dir.path().join(LEGACY_PROFILE_FILE), LEGACY_PROFILE);
635 assert!(
636 usable_profile_in(dir.path(), None, None, Some("GPU-slot0")).is_none(),
637 "a half-written profile for this card must not borrow the legacy file"
638 );
639 assert!(usable_profile_in(dir.path(), None, None, None).is_some());
642 }
643
644 #[test]
647 fn a_json_value_that_is_not_a_profile_counts_as_corrupt() {
648 let dir = TempDir::new("not-a-document");
649 write(
650 &dir.path().join(PROFILE_SUBDIR).join("GPU-slot0.json"),
651 "[1, 2, 3]",
652 );
653 write(&dir.path().join(LEGACY_PROFILE_FILE), LEGACY_PROFILE);
654 assert!(usable_profile_in(dir.path(), None, None, Some("GPU-slot0")).is_none());
655 }
656
657 #[test]
661 fn a_missing_per_gpu_profile_falls_through_to_the_legacy_file() {
662 let dir = TempDir::new("fallthrough");
663 write(&dir.path().join(LEGACY_PROFILE_FILE), LEGACY_PROFILE);
664 let profile = usable_profile_in(dir.path(), None, None, Some("GPU-slot0"))
665 .expect("the legacy file answers for an unbenched card");
666 assert_eq!(profile.fetch_fraction_for("nvfp4"), Some(0.2));
667 }
668
669 #[test]
671 fn the_per_gpu_profile_wins_over_the_legacy_file() {
672 let dir = TempDir::new("per-gpu-wins");
673 write(
674 &dir.path().join(PROFILE_SUBDIR).join("GPU-slot0.json"),
675 CARD_PROFILE,
676 );
677 write(&dir.path().join(LEGACY_PROFILE_FILE), LEGACY_PROFILE);
678 let profile = usable_profile_in(dir.path(), None, None, Some("GPU-slot0"))
679 .expect("the card's own profile is usable");
680 assert_eq!(profile.fetch_fraction_for("nvfp4"), Some(0.25));
681 assert_eq!(profile.backend_for("nvfp4"), Some(MoeBackend::Hybrid));
682 }
683
684 #[test]
688 fn an_explicit_path_is_the_only_candidate_considered() {
689 let dir = TempDir::new("explicit");
690 write(
691 &dir.path().join(PROFILE_SUBDIR).join("GPU-slot0.json"),
692 CARD_PROFILE,
693 );
694 write(&dir.path().join(LEGACY_PROFILE_FILE), LEGACY_PROFILE);
695
696 let absent = dir.path().join("typo.json");
697 assert!(usable_profile_in(dir.path(), None, Some(&absent), Some("GPU-slot0")).is_none());
698
699 let broken = dir.path().join("broken.json");
700 write(&broken, "not json at all");
701 assert!(usable_profile_in(dir.path(), None, Some(&broken), Some("GPU-slot0")).is_none());
702 }
703
704 #[test]
707 fn a_profile_measured_on_another_card_is_ignored() {
708 let dir = TempDir::new("other-card");
709 let path = dir.path().join("profile.json");
710 write(&path, CARD_PROFILE);
711 assert!(usable_profile_in(
712 dir.path(),
713 Some("NVIDIA GeForce RTX 4090"),
714 Some(&path),
715 None
716 )
717 .is_some());
718 assert!(
719 usable_profile_in(
720 dir.path(),
721 Some("NVIDIA GeForce RTX 3060 Ti"),
722 Some(&path),
723 None
724 )
725 .is_none(),
726 "another card's bandwidths are worse than no bandwidths"
727 );
728 }
729
730 #[test]
731 fn the_loaders_resolve_the_quant_name_before_the_lookup() {
732 let dir = TempDir::new("loaders");
733 let path = dir.path().join("profile.json");
734 write(&path, CARD_PROFILE);
735 assert_eq!(
736 load_hybrid_fetch_fraction("mxfp4", None, Some(&path), None),
737 Some(0.625),
738 "mxfp4 is benched under mxfp4_triton"
739 );
740 assert_eq!(
741 load_backend_recommendation("mxfp4", None, Some(&path), None),
742 Some(MoeBackend::Hybrid)
743 );
744 assert_eq!(
745 load_hybrid_fetch_fraction("nvfp4", None, Some(&path), None),
746 Some(0.25)
747 );
748 assert_eq!(
749 load_policy("nvfp4", None, Some(&path), None),
750 QStarPolicy::from_fraction(0.25)
751 );
752 }
753
754 #[test]
758 fn the_loaders_return_none_without_a_usable_profile() {
759 let dir = TempDir::new("no-profile");
760 let absent = dir.path().join("nothing.json");
761 assert_eq!(
762 load_backend_recommendation("nvfp4", None, Some(&absent), None),
763 None
764 );
765 assert_eq!(
766 load_hybrid_fetch_fraction("nvfp4", None, Some(&absent), None),
767 None
768 );
769 assert_eq!(
770 load_policy("nvfp4", None, Some(&absent), None),
771 QStarPolicy::fixed_cap(1)
772 );
773 }
774
775 #[test]
776 fn reading_a_profile_yields_the_document_or_nothing() {
777 let dir = TempDir::new("read");
778 let path = dir.path().join("profile.json");
779 write(&path, CARD_PROFILE);
780 let profile = read_profile(&path).expect("the fixture parses");
781 assert_eq!(profile.gpu.uuid.as_deref(), Some("GPU-slot0"));
782 assert_eq!(read_profile(&dir.path().join("absent.json")), None);
783 assert_eq!(read_profile(dir.path()), None, "a directory is not a file");
784 }
785
786 #[test]
789 fn the_cache_directory_is_absolute_and_ends_in_ferrox() {
790 let dir = cache_dir();
791 assert!(dir.is_absolute(), "{dir:?}");
792 assert_eq!(dir.file_name().and_then(|n| n.to_str()), Some("ferrox"));
793 }
794
795 #[test]
799 fn one_side_measured_is_not_a_measurement() {
800 assert_eq!(
801 entry_from(
802 &Measured {
803 cpu_moe_gbs: Some(50.0),
804 ..Measured::default()
805 },
806 1.0
807 ),
808 Err(NotMeasurable::OnlyOneSide)
809 );
810 assert_eq!(
811 entry_from(
812 &Measured {
813 pcie_gather_gbs: Some(20.0),
814 ..Measured::default()
815 },
816 1.0
817 ),
818 Err(NotMeasurable::OnlyOneSide)
819 );
820 assert_eq!(
823 entry_from(
824 &Measured {
825 cpu_moe_gbs: Some(50.0),
826 pcie_gather_gbs: Some(20.0),
827 cpu_moe_overlap_gbs: Some(30.0),
828 pcie_gather_overlap_gbs: None,
829 },
830 1.0
831 ),
832 Err(NotMeasurable::OnlyOneSide)
833 );
834 }
835
836 #[test]
840 fn a_bandwidth_at_or_below_zero_is_a_failed_measurement() {
841 for bad in [0.0, -1.0, f64::NAN, f64::INFINITY] {
842 assert_eq!(
843 entry_from(
844 &Measured {
845 cpu_moe_gbs: Some(bad),
846 pcie_gather_gbs: Some(20.0),
847 ..Measured::default()
848 },
849 1.0
850 ),
851 Err(NotMeasurable::NotPositive),
852 "{bad} must not become a profile entry"
853 );
854 }
855 }
856
857 #[test]
863 fn the_verdict_and_the_fraction_read_the_same_numbers() {
864 let measured = Measured {
867 cpu_moe_gbs: Some(100.0),
868 pcie_gather_gbs: Some(20.0),
869 cpu_moe_overlap_gbs: Some(10.0),
870 pcie_gather_overlap_gbs: Some(19.0),
871 };
872 let entry = entry_from(&measured, 1.0).expect("both sides measured");
873 assert_eq!(
874 entry.recommended,
875 Some(crate::qstar::MoeBackend::Offload),
876 "the contended pair is what the machine actually does"
877 );
878 let fraction = entry.fetch_fraction().expect("a pair exists");
880 let from_pair = crate::qstar::fetch_fraction_from_overlap(10.0, 19.0).unwrap();
881 assert!((fraction - from_pair).abs() < 1e-9);
882
883 let standalone = entry_from(
886 &Measured {
887 cpu_moe_gbs: Some(100.0),
888 pcie_gather_gbs: Some(20.0),
889 ..Measured::default()
890 },
891 1.0,
892 )
893 .unwrap();
894 assert_eq!(
895 standalone.recommended,
896 Some(crate::qstar::MoeBackend::Hybrid)
897 );
898 }
899
900 #[test]
905 fn a_profile_is_written_atomically_and_reads_back() {
906 let dir = std::env::temp_dir().join(format!(
907 "ferrox-benchbw-write-{}-{}",
908 std::process::id(),
909 line!()
910 ));
911 let _ = std::fs::remove_dir_all(&dir);
912 let path = default_profile_path_in(&dir, Some("GPU-abc"));
913
914 let mut profile = BandwidthProfile {
915 threshold: Some(1.0),
916 ..BandwidthProfile::default()
917 };
918 profile.gpu.name = Some("NVIDIA GeForce RTX 4090".to_string());
919 profile.gpu.uuid = Some("GPU-abc".to_string());
920 profile.dtype_kernels.insert(
921 "q4_k".to_string(),
922 entry_from(
923 &Measured {
924 cpu_moe_gbs: Some(80.0),
925 pcie_gather_gbs: Some(20.0),
926 ..Measured::default()
927 },
928 1.0,
929 )
930 .unwrap(),
931 );
932
933 write_profile(&path, &profile).expect("writes");
934 let read = read_profile(&path).expect("reads back");
935 assert_eq!(read.gpu.uuid.as_deref(), Some("GPU-abc"));
936 assert_eq!(
937 read.dtype_kernels["q4_k"].recommended,
938 Some(crate::qstar::MoeBackend::Hybrid)
939 );
940
941 assert!(
942 std::fs::read_dir(path.parent().unwrap())
943 .unwrap()
944 .all(|e| !e
945 .unwrap()
946 .file_name()
947 .to_string_lossy()
948 .ends_with(".partial")),
949 "nothing partial may survive a completed write"
950 );
951
952 assert!(read.matches_gpu(Some("NVIDIA GeForce RTX 4090")));
955 assert!(!read.matches_gpu(Some("NVIDIA GeForce RTX 3060 Ti")));
956
957 let _ = std::fs::remove_dir_all(&dir);
958 }
959}