benchmark: create some tree functions

This commit is contained in:
Sylvestre Ledru 2025-09-30 08:53:01 +02:00
parent 4180c00c73
commit c4d8b4b5bf
2 changed files with 86 additions and 73 deletions

View file

@ -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"]);
}

View file

@ -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;
}
}
}
}