slate_flash_calib/
slate_flash_calib.rs1use slate_kv::file_flash::{Durability, FileFlash};
22use slate_kv_hal::Flash;
23use std::fs::OpenOptions;
24use std::time::Instant;
25
26const CAPACITY: u32 = 4 * 1024 * 1024;
27const PAGE: usize = 256;
28const BLOCK: usize = 4096;
29const N: usize = 300;
30
31fn pct(sorted_ns: &[u64], q: f64) -> f64 {
32 let idx = ((q * sorted_ns.len() as f64).ceil() as usize).clamp(1, sorted_ns.len()) - 1;
33 sorted_ns[idx] as f64 / 1000.0
34}
35
36fn summarize(label: &str, mut ns: Vec<u64>) {
37 ns.sort_unstable();
38 let n = ns.len();
39 let mean = ns.iter().map(|&x| x as u128).sum::<u128>() as f64 / n as f64 / 1000.0;
40 println!(
41 "{label},{n},{mean:.3},{:.3},{:.3},{:.3},{:.3}",
42 pct(&ns, 0.50),
43 pct(&ns, 0.90),
44 pct(&ns, 0.99),
45 ns[n - 1] as f64 / 1000.0
46 );
47}
48
49fn time_flash_programs(dir: &std::path::Path, label: &str, dur: Durability) {
50 let path = dir.join(format!("calib_{label}.bin"));
51 let file = OpenOptions::new()
52 .read(true)
53 .write(true)
54 .create(true)
55 .truncate(false)
56 .open(&path)
57 .unwrap();
58 let mut flash = FileFlash::new(file, CAPACITY, PAGE, BLOCK, dur).unwrap();
59 let buf = [0xA5u8; PAGE];
60 let mut ns = Vec::with_capacity(N);
61 for i in 0..N {
64 let addr = (i * PAGE) as u32;
65 let t0 = Instant::now();
66 flash.program(addr, &buf).unwrap();
67 ns.push(t0.elapsed().as_nanos() as u64);
68 }
69 summarize(&format!("FileFlash::program,{label}"), ns);
70 drop(flash);
71 let _ = std::fs::remove_file(&path);
72}
73
74fn main() {
75 let dir = std::env::temp_dir().join(format!("slate_calib_{}", std::process::id()));
76 std::fs::create_dir_all(&dir).unwrap();
77
78 println!("# SLATE paper measurement: per-page flash barrier cost, HAL level, no engine");
79 println!(
80 "# platform=host os={} arch={} backend=FileFlash(file-backed emulation) NOT_real_NOR_flash",
81 std::env::consts::OS,
82 std::env::consts::ARCH
83 );
84 println!("# geometry: capacity={CAPACITY} page={PAGE} block={BLOCK} n_programs_per_mode={N}");
85 println!(
86 "# Durability::Full = fcntl(F_FULLFSYNC); Durability::OsCache = std File::sync_data(). \
87 raw_pwrite_no_barrier programs the same page size with no flush at all, as a floor."
88 );
89 println!("operation,mode,n,mean_us,p50_us,p90_us,p99_us,max_us");
90
91 time_flash_programs(&dir, "Full", Durability::Full);
92 time_flash_programs(&dir, "OsCache", Durability::OsCache);
93
94 {
99 use std::os::unix::fs::FileExt;
100 use std::os::unix::io::AsRawFd;
101 let path = dir.join("calib_barrier.bin");
102 let file = OpenOptions::new()
103 .read(true)
104 .write(true)
105 .create(true)
106 .truncate(true)
107 .open(&path)
108 .unwrap();
109 file.set_len(CAPACITY as u64).unwrap();
110 let buf = [0x5Au8; PAGE];
111 let fd = file.as_raw_fd();
112
113 let mut rust_sync_data = Vec::with_capacity(N);
114 let mut libc_fsync = Vec::with_capacity(N);
115 let mut libc_fullfsync = Vec::with_capacity(N);
116 for i in 0..N {
117 let off = ((i % 1024) * PAGE) as u64;
118 file.write_all_at(&buf, off).unwrap();
119 let t0 = Instant::now();
120 file.sync_data().unwrap();
121 rust_sync_data.push(t0.elapsed().as_nanos() as u64);
122
123 file.write_all_at(&buf, off).unwrap();
124 let t0 = Instant::now();
125 let rc = unsafe { libc::fsync(fd) };
126 libc_fsync.push(t0.elapsed().as_nanos() as u64);
127 assert_eq!(rc, 0);
128
129 file.write_all_at(&buf, off).unwrap();
135 let t0 = Instant::now();
136 #[cfg(target_os = "macos")]
137 let rc = unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) };
138 #[cfg(not(target_os = "macos"))]
139 let rc = unsafe { libc::fdatasync(fd) };
140 libc_fullfsync.push(t0.elapsed().as_nanos() as u64);
141 assert_ne!(rc, -1);
142 }
143 summarize("barrier_only,rust_File::sync_data", rust_sync_data);
144 summarize("barrier_only,libc_fsync", libc_fsync);
145 #[cfg(target_os = "macos")]
146 summarize("barrier_only,libc_fcntl_F_FULLFSYNC", libc_fullfsync);
147 #[cfg(not(target_os = "macos"))]
148 summarize("barrier_only,libc_fdatasync", libc_fullfsync);
149 drop(file);
150 let _ = std::fs::remove_file(&path);
151 }
152
153 {
155 use std::os::unix::fs::FileExt;
156 let path = dir.join("calib_raw.bin");
157 let file = OpenOptions::new()
158 .read(true)
159 .write(true)
160 .create(true)
161 .truncate(true)
162 .open(&path)
163 .unwrap();
164 file.set_len(CAPACITY as u64).unwrap();
165 let buf = [0xA5u8; PAGE];
166 let mut ns = Vec::with_capacity(N);
167 for i in 0..N {
168 let t0 = Instant::now();
169 file.write_all_at(&buf, (i * PAGE) as u64).unwrap();
170 ns.push(t0.elapsed().as_nanos() as u64);
171 }
172 summarize("raw_pwrite,no_barrier", ns);
173 drop(file);
174 let _ = std::fs::remove_file(&path);
175 }
176
177 let _ = std::fs::remove_dir_all(&dir);
178}