From c4d8b4b5bf69c62cac944fcb4bc96929fec899b8 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 30 Sep 2025 08:53:01 +0200 Subject: [PATCH] benchmark: create some tree functions --- src/uu/du/benches/du_bench.rs | 82 +++--------------------- src/uucore/src/lib/features/benchmark.rs | 77 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 73 deletions(-) diff --git a/src/uu/du/benches/du_bench.rs b/src/uu/du/benches/du_bench.rs index fbab5dfd7..a7c10822e 100644 --- a/src/uu/du/benches/du_bench.rs +++ b/src/uu/du/benches/du_bench.rs @@ -4,11 +4,9 @@ // file that was distributed with this source code. use divan::{Bencher, black_box}; -use std::fs::{self, File}; -use std::path::Path; use tempfile::TempDir; use uu_du::uumain; -use uucore::benchmark::run_util_function; +use uucore::benchmark::{fs_tree, run_util_function}; /// Helper to run du with given arguments on a directory fn bench_du_with_args(bencher: Bencher, temp_dir: &TempDir, args: &[&str]) { @@ -21,68 +19,6 @@ fn bench_du_with_args(bencher: Bencher, temp_dir: &TempDir, args: &[&str]) { }); } -/// Create a balanced directory tree for benchmarking -fn create_directory_tree( - base_dir: &Path, - depth: usize, - dirs_per_level: usize, - files_per_dir: usize, -) { - if depth == 0 { - return; - } - - // Create files in current directory - for file_idx in 0..files_per_dir { - let file_path = base_dir.join(format!("f{file_idx}")); - File::create(&file_path).unwrap(); - } - - // Create subdirectories and recurse - for dir_idx in 0..dirs_per_level { - let dir_path = base_dir.join(format!("d{dir_idx}")); - fs::create_dir(&dir_path).unwrap(); - create_directory_tree(&dir_path, depth - 1, dirs_per_level, files_per_dir); - } -} - -/// Create a wide directory tree (many files/dirs at shallow depth) -fn create_wide_tree(base_dir: &Path, total_files: usize, total_dirs: usize) { - // Create many files in root - for file_idx in 0..total_files { - let file_path = base_dir.join(format!("f{file_idx}")); - File::create(&file_path).unwrap(); - } - - // Create many directories with few files each - for dir_idx in 0..total_dirs { - let dir_path = base_dir.join(format!("d{dir_idx}")); - fs::create_dir(&dir_path).unwrap(); - for file_idx in 0..5 { - File::create(dir_path.join(format!("f{file_idx}"))).unwrap(); - } - } -} - -/// Create a deep directory tree (deep nesting) -fn create_deep_tree(base_dir: &Path, depth: usize, files_per_level: usize) { - 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 { - File::create(current_dir.join(format!("f{file_idx}"))).unwrap(); - } - - // Create next level directory - if level < depth - 1 { - let next_dir = current_dir.join("d"); - fs::create_dir(&next_dir).unwrap(); - current_dir = next_dir; - } - } -} - /// Benchmark default du on balanced tree #[divan::bench(args = [(5, 4, 10)])] fn du_balanced_tree( @@ -90,7 +26,7 @@ fn du_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); + fs_tree::create_balanced_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir); bench_du_with_args(bencher, &temp_dir, &[]); } @@ -101,7 +37,7 @@ fn du_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); + fs_tree::create_balanced_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir); bench_du_with_args(bencher, &temp_dir, &["-a"]); } @@ -112,7 +48,7 @@ fn du_human_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); + fs_tree::create_balanced_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir); bench_du_with_args(bencher, &temp_dir, &["-h"]); } @@ -120,7 +56,7 @@ fn du_human_balanced_tree( #[divan::bench(args = [(5000, 500)])] fn du_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); + fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs); bench_du_with_args(bencher, &temp_dir, &[]); } @@ -128,7 +64,7 @@ fn du_wide_tree(bencher: Bencher, (total_files, total_dirs): (usize, usize)) { #[divan::bench(args = [(5000, 500)])] fn du_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); + fs_tree::create_wide_tree(temp_dir.path(), total_files, total_dirs); bench_du_with_args(bencher, &temp_dir, &["-a"]); } @@ -136,7 +72,7 @@ fn du_all_wide_tree(bencher: Bencher, (total_files, total_dirs): (usize, usize)) #[divan::bench(args = [(100, 3)])] fn du_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); + fs_tree::create_deep_tree(temp_dir.path(), depth, files_per_level); bench_du_with_args(bencher, &temp_dir, &[]); } @@ -147,7 +83,7 @@ fn du_summarize_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); + fs_tree::create_balanced_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir); bench_du_with_args(bencher, &temp_dir, &["-s"]); } @@ -158,7 +94,7 @@ fn du_max_depth_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); + fs_tree::create_balanced_tree(temp_dir.path(), depth, dirs_per_level, files_per_dir); bench_du_with_args(bencher, &temp_dir, &["--max-depth=2"]); } diff --git a/src/uucore/src/lib/features/benchmark.rs b/src/uucore/src/lib/features/benchmark.rs index 5f87540ae..2a367fae9 100644 --- a/src/uucore/src/lib/features/benchmark.rs +++ b/src/uucore/src/lib/features/benchmark.rs @@ -307,3 +307,80 @@ pub mod text_data { .join("\n") } } + +/// Filesystem tree generation utilities for benchmarking +pub mod fs_tree { + use std::fs::{self, File}; + use std::path::Path; + + /// Create a balanced directory tree for benchmarking + /// + /// Creates a tree with specified depth, number of directories per level, and files per directory. + /// This creates a realistic filesystem structure for testing recursive operations. + pub fn create_balanced_tree( + base_dir: &Path, + depth: usize, + dirs_per_level: usize, + files_per_dir: usize, + ) { + if depth == 0 { + return; + } + + // Create files in current directory + for file_idx in 0..files_per_dir { + let file_path = base_dir.join(format!("f{file_idx}")); + File::create(&file_path).unwrap(); + } + + // Create subdirectories and recurse + for dir_idx in 0..dirs_per_level { + let dir_path = base_dir.join(format!("d{dir_idx}")); + fs::create_dir(&dir_path).unwrap(); + create_balanced_tree(&dir_path, depth - 1, dirs_per_level, files_per_dir); + } + } + + /// Create a wide directory tree (many files/dirs at shallow depth) + /// + /// This creates a flat structure with many files and directories at a shallow depth, + /// useful for benchmarking operations that need to traverse many entries quickly. + pub fn create_wide_tree(base_dir: &Path, total_files: usize, total_dirs: usize) { + // Create many files in root + for file_idx in 0..total_files { + let file_path = base_dir.join(format!("f{file_idx}")); + File::create(&file_path).unwrap(); + } + + // Create many directories with few files each + for dir_idx in 0..total_dirs { + let dir_path = base_dir.join(format!("d{dir_idx}")); + fs::create_dir(&dir_path).unwrap(); + for file_idx in 0..5 { + File::create(dir_path.join(format!("f{file_idx}"))).unwrap(); + } + } + } + + /// Create a deep directory tree (deep nesting) + /// + /// This creates a linear chain of deeply nested directories, useful for testing + /// recursion depth handling and stack usage. + pub fn create_deep_tree(base_dir: &Path, depth: usize, files_per_level: usize) { + 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 { + File::create(current_dir.join(format!("f{file_idx}"))).unwrap(); + } + + // Create next level directory + if level < depth - 1 { + let next_dir = current_dir.join("d"); + fs::create_dir(&next_dir).unwrap(); + current_dir = next_dir; + } + } + } +}