From bfaf5f7aefc080bf4c503522b21d250d02db20dd Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 30 Sep 2025 15:35:59 +0200 Subject: [PATCH] bench: remove some duplication --- src/uu/ls/benches/ls_bench.rs | 147 ++--------------------- src/uucore/src/lib/features/benchmark.rs | 47 ++++++++ 2 files changed, 59 insertions(+), 135 deletions(-) diff --git a/src/uu/ls/benches/ls_bench.rs b/src/uu/ls/benches/ls_bench.rs index 9777291cd..b837c2b7c 100644 --- a/src/uu/ls/benches/ls_bench.rs +++ b/src/uu/ls/benches/ls_bench.rs @@ -4,12 +4,10 @@ // file that was distributed with this source code. use divan::{Bencher, black_box}; -use std::fs::{self, File}; -use std::io::Write; -use std::path::Path; +use std::fs; use tempfile::TempDir; use uu_ls::uumain; -use uucore::benchmark::run_util_function; +use uucore::benchmark::{fs_tree, run_util_function}; /// Helper to run ls with given arguments on a directory fn bench_ls_with_args(bencher: Bencher, temp_dir: &TempDir, args: &[&str]) { @@ -23,127 +21,6 @@ fn bench_ls_with_args(bencher: Bencher, temp_dir: &TempDir, args: &[&str]) { }); } -/// Create a deterministic directory tree for benchmarking ls -R performance -fn create_directory_tree( - base_dir: &Path, - depth: usize, - dirs_per_level: usize, - files_per_dir: usize, -) -> std::io::Result<()> { - if depth == 0 { - return Ok(()); - } - - // Create files in current directory - for file_idx in 0..files_per_dir { - let file_path = base_dir.join(format!("file_{file_idx:04}.txt")); - let mut file = File::create(&file_path)?; - writeln!(file, "This is file {file_idx} at depth {depth}")?; - } - - // Create subdirectories and recurse - for dir_idx in 0..dirs_per_level { - let dir_path = base_dir.join(format!("subdir_{dir_idx:04}")); - fs::create_dir(&dir_path)?; - create_directory_tree(&dir_path, depth - 1, dirs_per_level, files_per_dir)?; - } - - Ok(()) -} - -/// Create a wide directory tree (many files/dirs at shallow depth) -fn create_wide_tree(base_dir: &Path, total_files: usize, total_dirs: usize) -> std::io::Result<()> { - // Create many files in root - for file_idx in 0..total_files { - let file_path = base_dir.join(format!("wide_file_{file_idx:06}.txt")); - let mut file = File::create(&file_path)?; - writeln!(file, "Wide tree file {file_idx}")?; - } - - // Create many directories with few files each - let files_per_subdir = 5; - for dir_idx in 0..total_dirs { - let dir_path = base_dir.join(format!("wide_dir_{dir_idx:06}")); - fs::create_dir(&dir_path)?; - - for file_idx in 0..files_per_subdir { - let file_path = dir_path.join(format!("file_{file_idx}.txt")); - let mut file = File::create(&file_path)?; - writeln!(file, "File {file_idx} in wide dir {dir_idx}")?; - } - } - - Ok(()) -} - -/// Create a deep directory tree (few files/dirs but deep nesting) -fn create_deep_tree(base_dir: &Path, depth: usize, files_per_level: usize) -> std::io::Result<()> { - let mut current_dir = base_dir.to_path_buf(); - - for level in 0..depth { - // Create files at this level - for file_idx in 0..files_per_level { - let file_path = current_dir.join(format!("deep_file_{level}_{file_idx}.txt")); - let mut file = File::create(&file_path)?; - writeln!(file, "File {file_idx} at depth level {level}")?; - } - - // Create next level directory - if level < depth - 1 { - let next_dir = current_dir.join(format!("level_{:04}", level + 1)); - fs::create_dir(&next_dir)?; - current_dir = next_dir; - } - } - - Ok(()) -} - -/// Create a tree with mixed file types and permissions for comprehensive testing -fn create_mixed_tree(base_dir: &Path) -> std::io::Result<()> { - let extensions = ["txt", "log", "dat", "tmp", "bak", "cfg"]; - let sizes = [0, 100, 1024, 10240]; - - for (i, ext) in extensions.iter().enumerate() { - for (j, &size) in sizes.iter().enumerate() { - let file_path = base_dir.join(format!("mixed_file_{i}_{j}.{ext}")); - let mut file = File::create(&file_path)?; - - if size > 0 { - let content = "x".repeat(size); - file.write_all(content.as_bytes())?; - } - - // Set permissions only on Unix platforms - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - let perms = fs::Permissions::from_mode(match (i + j) % 4 { - 0 => 0o644, - 1 => 0o755, - 2 => 0o600, - _ => 0o444, - }); - fs::set_permissions(&file_path, perms)?; - } - } - } - - // Create some subdirectories - for i in 0..5 { - let dir_path = base_dir.join(format!("mixed_subdir_{i}")); - fs::create_dir(&dir_path)?; - - for j in 0..3 { - let file_path = dir_path.join(format!("sub_file_{j}.txt")); - let mut file = File::create(&file_path)?; - writeln!(file, "File {j} in subdir {i}")?; - } - } - - Ok(()) -} - /// Benchmark ls -R on balanced directory tree #[divan::bench(args = [(6, 4, 15)])] fn ls_recursive_balanced_tree( @@ -151,7 +28,7 @@ fn ls_recursive_balanced_tree( (depth, dirs_per_level, files_per_dir): (usize, usize, usize), ) { let temp_dir = TempDir::new().unwrap(); - create_directory_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir).unwrap(); + fs_tree::create_balanced_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir); bench_ls_with_args(bencher, &temp_dir, &[]); } @@ -162,7 +39,7 @@ fn ls_recursive_long_all_balanced_tree( (depth, dirs_per_level, files_per_dir): (usize, usize, usize), ) { let temp_dir = TempDir::new().unwrap(); - create_directory_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir).unwrap(); + fs_tree::create_balanced_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir); bench_ls_with_args(bencher, &temp_dir, &["-a", "-l"]); } @@ -170,7 +47,7 @@ fn ls_recursive_long_all_balanced_tree( #[divan::bench(args = [(10000, 1000)])] fn ls_recursive_wide_tree(bencher: Bencher, (total_files, total_dirs): (usize, usize)) { let temp_dir = TempDir::new().unwrap(); - create_wide_tree(temp_dir.path(), total_files, total_dirs).unwrap(); + fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs); bench_ls_with_args(bencher, &temp_dir, &[]); } @@ -178,7 +55,7 @@ fn ls_recursive_wide_tree(bencher: Bencher, (total_files, total_dirs): (usize, u #[divan::bench(args = [(15000, 1500)])] fn ls_recursive_long_all_wide_tree(bencher: Bencher, (total_files, total_dirs): (usize, usize)) { let temp_dir = TempDir::new().unwrap(); - create_wide_tree(temp_dir.path(), total_files, total_dirs).unwrap(); + fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs); bench_ls_with_args(bencher, &temp_dir, &["-a", "-l"]); } @@ -186,7 +63,7 @@ fn ls_recursive_long_all_wide_tree(bencher: Bencher, (total_files, total_dirs): #[divan::bench(args = [(200, 2)])] fn ls_recursive_deep_tree(bencher: Bencher, (depth, files_per_level): (usize, usize)) { let temp_dir = TempDir::new().unwrap(); - create_deep_tree(temp_dir.path(), depth, files_per_level).unwrap(); + fs_tree::create_deep_tree(temp_dir.path(), depth, files_per_level); bench_ls_with_args(bencher, &temp_dir, &[]); } @@ -194,7 +71,7 @@ fn ls_recursive_deep_tree(bencher: Bencher, (depth, files_per_level): (usize, us #[divan::bench(args = [(100, 4)])] fn ls_recursive_long_all_deep_tree(bencher: Bencher, (depth, files_per_level): (usize, usize)) { let temp_dir = TempDir::new().unwrap(); - create_deep_tree(temp_dir.path(), depth, files_per_level).unwrap(); + fs_tree::create_deep_tree(temp_dir.path(), depth, files_per_level); bench_ls_with_args(bencher, &temp_dir, &["-a", "-l"]); } @@ -202,12 +79,12 @@ fn ls_recursive_long_all_deep_tree(bencher: Bencher, (depth, files_per_level): ( #[divan::bench] fn ls_recursive_mixed_tree(bencher: Bencher) { let temp_dir = TempDir::new().unwrap(); - create_mixed_tree(temp_dir.path()).unwrap(); + fs_tree::create_mixed_tree(temp_dir.path()); for i in 0..10 { let subdir = temp_dir.path().join(format!("mixed_branch_{i}")); fs::create_dir(&subdir).unwrap(); - create_mixed_tree(&subdir).unwrap(); + fs_tree::create_mixed_tree(&subdir); } bench_ls_with_args(bencher, &temp_dir, &[]); @@ -217,12 +94,12 @@ fn ls_recursive_mixed_tree(bencher: Bencher) { #[divan::bench] fn ls_recursive_long_all_mixed_tree(bencher: Bencher) { let temp_dir = TempDir::new().unwrap(); - create_mixed_tree(temp_dir.path()).unwrap(); + fs_tree::create_mixed_tree(temp_dir.path()); for i in 0..10 { let subdir = temp_dir.path().join(format!("mixed_branch_{i}")); fs::create_dir(&subdir).unwrap(); - create_mixed_tree(&subdir).unwrap(); + fs_tree::create_mixed_tree(&subdir); } bench_ls_with_args(bencher, &temp_dir, &["-a", "-l"]); diff --git a/src/uucore/src/lib/features/benchmark.rs b/src/uucore/src/lib/features/benchmark.rs index 2a367fae9..0ca60d2d7 100644 --- a/src/uucore/src/lib/features/benchmark.rs +++ b/src/uucore/src/lib/features/benchmark.rs @@ -311,6 +311,7 @@ pub mod text_data { /// Filesystem tree generation utilities for benchmarking pub mod fs_tree { use std::fs::{self, File}; + use std::io::Write; use std::path::Path; /// Create a balanced directory tree for benchmarking @@ -383,4 +384,50 @@ pub mod fs_tree { } } } + + /// Create a tree with mixed file types and permissions for comprehensive testing + /// + /// Creates files with different extensions, sizes, and permissions (on Unix). + /// Useful for testing file type detection, permission handling, and formatting. + pub fn create_mixed_tree(base_dir: &Path) { + let extensions = ["txt", "log", "dat", "tmp", "bak", "cfg"]; + let sizes = [0, 100, 1024, 10240]; + + for (i, ext) in extensions.iter().enumerate() { + for (j, &size) in sizes.iter().enumerate() { + let file_path = base_dir.join(format!("mixed_file_{i}_{j}.{ext}")); + let mut file = File::create(&file_path).unwrap(); + + if size > 0 { + let content = "x".repeat(size); + file.write_all(content.as_bytes()).unwrap(); + } + + // Set permissions only on Unix platforms + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let perms = fs::Permissions::from_mode(match (i + j) % 4 { + 0 => 0o644, + 1 => 0o755, + 2 => 0o600, + _ => 0o444, + }); + fs::set_permissions(&file_path, perms).unwrap(); + } + } + } + + // Create some subdirectories + for i in 0..5 { + let dir_path = base_dir.join(format!("mixed_subdir_{i}")); + fs::create_dir(&dir_path).unwrap(); + + for j in 0..3 { + let file_path = dir_path.join(format!("sub_file_{j}.txt")); + let mut file = File::create(&file_path).unwrap(); + writeln!(file, "File {j} in subdir {i}").unwrap(); + } + } + } }