11mod common;
22
3- const FILE_SIZE : usize = 50 * 1024 * 1024 ; // 50 MB
4- const WRITE_SIZE : usize = 500 * 1024 * 1024 ; // 500 MB for write benchmarks
3+ const BENCH_SIZES : & [ ( usize , & str ) ] = & [
4+ ( 50 * 1024 * 1024 , "50MB" ) ,
5+ ( 200 * 1024 * 1024 , "200MB" ) ,
6+ ( 500 * 1024 * 1024 , "500MB" ) ,
7+ ] ;
58
69#[ tokio:: test]
710async fn test_bench ( ) {
8- let test_filename = format ! ( "bench_{}.bin" , std:: process:: id( ) ) ;
9- let expected = common:: generate_pattern ( FILE_SIZE ) ;
10-
11- let ( token, bucket_id, hub) = match common:: setup_bucket_with_file ( "bench" , & test_filename, & expected) . await {
11+ // Upload files of each size to the same bucket
12+ let ( token, bucket_id, hub) = match common:: setup_bucket ( "bench" ) . await {
1213 Some ( cfg) => cfg,
1314 None => return ,
1415 } ;
1516
17+ let mut files: Vec < ( String , Vec < u8 > ) > = Vec :: new ( ) ;
18+ for & ( size, label) in BENCH_SIZES {
19+ let filename = format ! ( "bench_{}.bin" , label) ;
20+ let data = common:: generate_pattern ( size) ;
21+ let write_config = common:: build_write_config ( & hub) . await ;
22+
23+ let tmp_dir = std:: env:: temp_dir ( ) . join ( format ! ( "hf-mount-bench-setup-{}" , label) ) ;
24+ std:: fs:: create_dir_all ( & tmp_dir) . ok ( ) ;
25+ let staging_path = tmp_dir. join ( & filename) ;
26+ std:: fs:: write ( & staging_path, & data) . expect ( "write staging file" ) ;
27+
28+ let file_info = common:: upload_file ( write_config, & staging_path) . await ;
29+ let xet_hash = file_info. hash ( ) . to_string ( ) ;
30+ eprintln ! ( "Uploaded {}: xet_hash={}" , label, xet_hash) ;
31+
32+ let mtime_ms = std:: time:: SystemTime :: now ( )
33+ . duration_since ( std:: time:: UNIX_EPOCH )
34+ . unwrap_or_default ( )
35+ . as_millis ( ) as u64 ;
36+
37+ hub. batch_operations ( & [ hf_mount:: hub_api:: BatchOp :: AddFile {
38+ path : filename. clone ( ) ,
39+ xet_hash,
40+ mtime : mtime_ms,
41+ content_type : None ,
42+ } ] )
43+ . await
44+ . expect ( "batch add failed" ) ;
45+
46+ std:: fs:: remove_dir_all ( & tmp_dir) . ok ( ) ;
47+ files. push ( ( filename, data) ) ;
48+ }
49+
1650 let pid = std:: process:: id ( ) ;
1751
18- // --- FUSE benchmark (read + write) ---
52+ // --- FUSE benchmark (read + write per size ) ---
1953 let fuse_mount = format ! ( "/tmp/hf-bench-fuse-{}" , pid) ;
2054 let fuse_cache = format ! ( "/tmp/hf-bench-fuse-cache-{}" , pid) ;
2155
2256 let fuse_result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
2357 let child = common:: mount_bucket ( & bucket_id, & fuse_mount, & fuse_cache, & [ ] ) ;
24- let r = common:: bench:: run_read_benchmarks ( & fuse_mount, & test_filename, & expected) ;
2558
26- // Write benchmark: 50 MB sequential write
27- let write_result = common:: bench:: run_write_benchmark ( & fuse_mount, WRITE_SIZE ) ;
59+ let mut reads = Vec :: new ( ) ;
60+ for ( filename, expected) in & files {
61+ reads. push ( common:: bench:: run_read_benchmarks ( & fuse_mount, filename, expected) ) ;
62+ }
63+
64+ let mut writes = Vec :: new ( ) ;
65+ for & ( size, _) in BENCH_SIZES {
66+ writes. push ( common:: bench:: run_write_benchmark ( & fuse_mount, size) ) ;
67+ }
2868
2969 common:: unmount ( & fuse_mount, child, 60 ) ;
30- ( r , write_result )
70+ ( reads , writes )
3171 } ) ) ;
3272
3373 std:: fs:: remove_dir_all ( & fuse_mount) . ok ( ) ;
@@ -39,161 +79,87 @@ async fn test_bench() {
3979
4080 let nfs_result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
4181 let child = common:: mount_bucket_nfs ( & bucket_id, & nfs_mount, & nfs_cache, & [ "--read-only" ] ) ;
42- let r = common:: bench:: run_read_benchmarks ( & nfs_mount, & test_filename, & expected) ;
82+ let mut reads = Vec :: new ( ) ;
83+ for ( filename, expected) in & files {
84+ reads. push ( common:: bench:: run_read_benchmarks ( & nfs_mount, filename, expected) ) ;
85+ }
4386 common:: unmount_nfs ( & nfs_mount, child, 5 ) ;
44- r
87+ reads
4588 } ) ) ;
4689
4790 std:: fs:: remove_dir_all ( & nfs_mount) . ok ( ) ;
4891 std:: fs:: remove_dir_all ( & nfs_cache) . ok ( ) ;
4992
50- // --- mount-s3 reference benchmark (read only, requires mount-s3 + AWS creds) ---
51- let s3_result = if common:: s3_bench_available ( ) {
52- let s3_bucket = common:: setup_s3_bench ( & expected) ;
53- let s3_mount = format ! ( "/tmp/hf-bench-s3-{}" , pid) ;
54-
55- let r = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
56- let child = common:: mount_s3 ( & s3_bucket, & s3_mount) ;
57- let r = common:: bench:: run_read_benchmarks ( & s3_mount, "bench_file.bin" , & expected) ;
58- common:: cleanup_s3_bench ( & s3_mount, child, & s3_bucket) ;
59- r
60- } ) ) ;
61-
62- match r {
63- Ok ( Ok ( bench) ) => Some ( bench) ,
64- Ok ( Err ( e) ) => {
65- eprintln ! ( "mount-s3 bench error: {}" , e) ;
66- None
67- }
68- Err ( e) => std:: panic:: resume_unwind ( e) ,
69- }
70- } else {
71- eprintln ! ( "Skipping mount-s3 bench (mount-s3 binary or AWS creds not available)" ) ;
72- None
73- } ;
74-
75- // --- Raw cleaner benchmark (no FUSE, no kernel) ---
76- let raw_mbps = {
77- let write_config = common:: build_write_config ( & hub) . await ;
78- let download_session = data:: FileDownloadSession :: new ( write_config. clone ( ) , None , None )
79- . await
80- . expect ( "download session" ) ;
81- let xet_sessions = hf_mount:: xet:: XetSessions :: new ( download_session, Some ( write_config) ) ;
82- match common:: bench:: run_raw_cleaner_benchmark ( & xet_sessions, WRITE_SIZE ) . await {
83- Ok ( mbps) => Some ( mbps) ,
84- Err ( e) => {
85- eprintln ! ( "Raw cleaner bench error: {}" , e) ;
86- None
87- }
88- }
89- } ;
90-
91- // Cleanup bucket
93+ // Cleanup
9294 common:: delete_bucket ( & common:: endpoint ( ) , & token, & bucket_id) . await ;
9395
9496 // --- Extract results ---
95- let ( fuse, write) = match fuse_result {
96- Ok ( ( Ok ( r) , w) ) => ( Some ( r) , w) ,
97- Ok ( ( Err ( e) , _) ) => {
98- eprintln ! ( "FUSE bench error: {}" , e) ;
99- ( None , Err ( e) )
100- }
97+ let ( fuse_reads, fuse_writes) = match fuse_result {
98+ Ok ( ( reads, writes) ) => ( reads, writes) ,
10199 Err ( e) => std:: panic:: resume_unwind ( e) ,
102100 } ;
103101
104- let nfs = match nfs_result {
105- Ok ( Ok ( r) ) => Some ( r) ,
106- Ok ( Err ( e) ) => {
107- eprintln ! ( "NFS bench error: {}" , e) ;
108- None
109- }
102+ let nfs_reads = match nfs_result {
103+ Ok ( reads) => reads,
110104 Err ( e) => std:: panic:: resume_unwind ( e) ,
111105 } ;
112106
113- // --- Print comparison table ---
114- eprintln ! ( "\n ============================================================" ) ;
115- eprintln ! ( " FUSE vs NFS vs mount-s3 Benchmark — 50 MB file" ) ;
116- eprintln ! ( "============================================================" ) ;
117- eprintln ! ( " {:30} {:>12} {:>12} {:>12}" , "Metric" , "FUSE" , "NFS" , "mount-s3" ) ;
118- eprintln ! ( " {:-<30} {:-<12} {:-<12} {:-<12}" , "" , "" , "" , "" ) ;
119-
120- if let ( Some ( f) , Some ( n) ) = ( & fuse, & nfs) {
121- let s3_read = s3_result. as_ref ( ) . map ( |s| format ! ( "{:>9.1} MB/s" , s. seq_read_mbps) ) ;
122- let s3_reread = s3_result. as_ref ( ) . map ( |s| format ! ( "{:>9.1} MB/s" , s. seq_reread_mbps) ) ;
123- let s3_range = s3_result. as_ref ( ) . map ( |s| format ! ( "{:>9.1} ms" , s. range_read_ms) ) ;
124- let s3_rand = s3_result. as_ref ( ) . map ( |s| format ! ( "{:>9.1} ms" , s. random_avg_ms) ) ;
125- let dash = " —" . to_string ( ) ;
126-
127- eprintln ! (
128- " {:30} {:>9.1} MB/s {:>9.1} MB/s {:>12}" ,
129- "Sequential read" ,
130- f. seq_read_mbps,
131- n. seq_read_mbps,
132- s3_read. as_deref( ) . unwrap_or( & dash)
133- ) ;
134- eprintln ! (
135- " {:30} {:>9.1} MB/s {:>9.1} MB/s {:>12}" ,
136- "Sequential re-read" ,
137- f. seq_reread_mbps,
138- n. seq_reread_mbps,
139- s3_reread. as_deref( ) . unwrap_or( & dash)
140- ) ;
141- eprintln ! (
142- " {:30} {:>9.1} ms {:>9.1} ms {:>12}" ,
143- "Range read (1MB@25MB)" ,
144- f. range_read_ms,
145- n. range_read_ms,
146- s3_range. as_deref( ) . unwrap_or( & dash)
147- ) ;
148- eprintln ! (
149- " {:30} {:>9.1} ms {:>9.1} ms {:>12}" ,
150- "Random reads (100x4KB avg)" ,
151- f. random_avg_ms,
152- n. random_avg_ms,
153- s3_rand. as_deref( ) . unwrap_or( & dash)
154- ) ;
155- let f_speedup = if f. neg_cache_second_ms > 0.0 {
156- f. neg_cache_first_ms / f. neg_cache_second_ms
157- } else {
158- f64:: INFINITY
159- } ;
160- let n_speedup = if n. neg_cache_second_ms > 0.0 {
161- n. neg_cache_first_ms / n. neg_cache_second_ms
107+ // --- Print tables per size ---
108+ for ( i, & ( _, label) ) in BENCH_SIZES . iter ( ) . enumerate ( ) {
109+ let fuse = fuse_reads[ i] . as_ref ( ) . ok ( ) ;
110+ let nfs = nfs_reads[ i] . as_ref ( ) . ok ( ) ;
111+ let write = fuse_writes[ i] . as_ref ( ) . ok ( ) ;
112+
113+ eprintln ! ( "\n ============================================================" ) ;
114+ eprintln ! ( " Benchmark — {}" , label) ;
115+ eprintln ! ( "------------------------------------------------------------" ) ;
116+ eprintln ! ( " {:30} {:>12} {:>12}" , "Metric" , "FUSE" , "NFS" ) ;
117+ eprintln ! ( " {:-<30} {:-<12} {:-<12}" , "" , "" , "" ) ;
118+
119+ if let ( Some ( f) , Some ( n) ) = ( fuse, nfs) {
120+ eprintln ! (
121+ " {:30} {:>9.1} MB/s {:>9.1} MB/s" ,
122+ "Sequential read" , f. seq_read_mbps, n. seq_read_mbps
123+ ) ;
124+ eprintln ! (
125+ " {:30} {:>9.1} MB/s {:>9.1} MB/s" ,
126+ "Sequential re-read" , f. seq_reread_mbps, n. seq_reread_mbps
127+ ) ;
128+ eprintln ! (
129+ " {:30} {:>9.1} ms {:>9.1} ms" ,
130+ "Range read (1MB@25MB)" , f. range_read_ms, n. range_read_ms
131+ ) ;
132+ eprintln ! (
133+ " {:30} {:>9.1} ms {:>9.1} ms" ,
134+ "Random reads (100x4KB avg)" , f. random_avg_ms, n. random_avg_ms
135+ ) ;
162136 } else {
163- f64:: INFINITY
164- } ;
165- let s3_speedup = s3_result. as_ref ( ) . map ( |s| {
166- if s. neg_cache_second_ms > 0.0 {
167- format ! ( "{:>9.1}x" , s. neg_cache_first_ms / s. neg_cache_second_ms)
168- } else {
169- " inf" . to_string ( )
137+ if fuse. is_none ( ) {
138+ eprintln ! ( " FUSE read: FAILED" ) ;
170139 }
171- } ) ;
172- eprintln ! (
173- " {:30} {:>9.1}x {:>9.1}x {:>12}" ,
174- "Neg cache speedup" ,
175- f_speedup,
176- n_speedup,
177- s3_speedup. as_deref( ) . unwrap_or( & dash)
178- ) ;
179- }
140+ if nfs. is_none ( ) {
141+ eprintln ! ( " NFS read: FAILED" ) ;
142+ }
143+ }
180144
181- if let Ok ( ref w) = write {
182- eprintln ! ( " {:30} {:>9.1} MB/s" , "Sequential write (500MB)" , w. write_mbps) ;
183- eprintln ! ( " {:30} {:>9.3} s" , "Close latency (CAS+Hub)" , w. close_secs) ;
184- eprintln ! ( " {:30} {:>9.1} MB/s" , "Write end-to-end" , w. total_mbps) ;
185- eprintln ! ( " {:30} {:>9.1} MB/s" , "Dedup write (same data)" , w. dedup_write_mbps) ;
186- eprintln ! ( " {:30} {:>9.3} s" , "Dedup close latency" , w. dedup_close_secs) ;
187- eprintln ! ( " {:30} {:>9.1} MB/s" , "Dedup end-to-end" , w. dedup_total_mbps) ;
188- } else {
189- eprintln ! ( " {:30} {:>12}" , "Write benchmark" , "FAIL" ) ;
190- }
191- if let Some ( raw) = raw_mbps {
192- eprintln ! ( " {:30} {:>9.1} MB/s" , "Raw add_data() (no FUSE)" , raw) ;
145+ if let Some ( w) = write {
146+ eprintln ! ( " {:30} {:>9.1} MB/s" , "Sequential write (FUSE)" , w. write_mbps) ;
147+ eprintln ! ( " {:30} {:>9.3} s" , "Close latency (CAS+Hub)" , w. close_secs) ;
148+ eprintln ! ( " {:30} {:>9.1} MB/s" , "Write end-to-end" , w. total_mbps) ;
149+ eprintln ! ( " {:30} {:>9.1} MB/s" , "Dedup write" , w. dedup_write_mbps) ;
150+ eprintln ! ( " {:30} {:>9.3} s" , "Dedup close latency" , w. dedup_close_secs) ;
151+ eprintln ! ( " {:30} {:>9.1} MB/s" , "Dedup end-to-end" , w. dedup_total_mbps) ;
152+ } else {
153+ eprintln ! ( " Write: FAILED" ) ;
154+ }
155+ eprintln ! ( "============================================================" ) ;
193156 }
194- eprintln ! ( "============================================================ \n " ) ;
157+ eprintln ! ( ) ;
195158
196- assert ! ( fuse. is_some( ) , "FUSE benchmark failed" ) ;
197- assert ! ( nfs. is_some( ) , "NFS benchmark failed" ) ;
198- write. expect ( "Write benchmark failed" ) ;
159+ // Assertions
160+ for ( i, & ( _, label) ) in BENCH_SIZES . iter ( ) . enumerate ( ) {
161+ assert ! ( fuse_reads[ i] . is_ok( ) , "FUSE read benchmark failed for {}" , label) ;
162+ assert ! ( nfs_reads[ i] . is_ok( ) , "NFS read benchmark failed for {}" , label) ;
163+ assert ! ( fuse_writes[ i] . is_ok( ) , "Write benchmark failed for {}" , label) ;
164+ }
199165}
0 commit comments