feat: Parallele Verarbeitung mit rayon bei vielen Dateien
- Bei >= 100 Dateien wird rayon für parallele Berechnung genutzt - clean_filename() wird parallel ausgeführt (CPU-intensive Operation) - Tatsächliche Umbenennung bleibt sequenziell (I/O-bound, Race-Conditions vermeiden) - Bei < 100 Dateien sequenziell (Overhead vermeiden) - RenameOperation-Struct für geplante Umbenennungen Performance-Gewinn bei großen Verzeichnisbäumen mit vielen Dateien. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
835e9b8761
commit
de7c91a52a
1 changed files with 84 additions and 42 deletions
126
src/main.rs
126
src/main.rs
|
|
@ -10,8 +10,10 @@ use config::Config;
|
||||||
use glob::Pattern;
|
use glob::Pattern;
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
|
use rayon::prelude::*;
|
||||||
use sanitizer::{clean_filename, is_excluded, is_safe_rename};
|
use sanitizer::{clean_filename, is_excluded, is_safe_rename};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
// Standard-Ausschlussmuster (ähnlich wie detox)
|
// Standard-Ausschlussmuster (ähnlich wie detox)
|
||||||
|
|
@ -28,6 +30,16 @@ const DEFAULT_EXCLUDES: &[&str] = &[
|
||||||
"__pycache__/**",
|
"__pycache__/**",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Schwellwert für parallele Verarbeitung (bei weniger Dateien lohnt sich Overhead nicht)
|
||||||
|
const PARALLEL_THRESHOLD: usize = 100;
|
||||||
|
|
||||||
|
/// Repräsentiert eine geplante Umbenennungsoperation
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RenameOperation {
|
||||||
|
old_path: PathBuf,
|
||||||
|
new_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
/// Startpunkt des Programms
|
/// Startpunkt des Programms
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// Initialisiere Logger
|
// Initialisiere Logger
|
||||||
|
|
@ -92,56 +104,86 @@ fn main() -> Result<()> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Umbenennen in absteigender Tiefe
|
// Berechne Umbenennungen (parallel bei vielen Dateien)
|
||||||
for entry in entries {
|
let rename_ops: Vec<RenameOperation> = if entries.len() >= PARALLEL_THRESHOLD {
|
||||||
|
// Parallel mit rayon
|
||||||
|
entries
|
||||||
|
.par_iter()
|
||||||
|
.filter_map(|entry| {
|
||||||
|
let old_path = entry.path();
|
||||||
|
|
||||||
|
// Ebenentiefe 0 -> überspringen
|
||||||
|
if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special Files nur mit --special
|
||||||
|
let file_type = entry.file_type();
|
||||||
|
if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dateiname ermitteln und bereinigen
|
||||||
|
let filename = old_path.file_name()?;
|
||||||
|
let new_name = clean_filename(filename, &config, false)?;
|
||||||
|
let new_path = old_path.with_file_name(&new_name);
|
||||||
|
|
||||||
|
Some(RenameOperation {
|
||||||
|
old_path: old_path.to_path_buf(),
|
||||||
|
new_path,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
// Sequenziell bei wenigen Dateien
|
||||||
|
entries
|
||||||
|
.iter()
|
||||||
|
.filter_map(|entry| {
|
||||||
|
let old_path = entry.path();
|
||||||
|
|
||||||
|
if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_type = entry.file_type();
|
||||||
|
if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = old_path.file_name()?;
|
||||||
|
let new_name = clean_filename(filename, &config, false)?;
|
||||||
|
let new_path = old_path.with_file_name(&new_name);
|
||||||
|
|
||||||
|
Some(RenameOperation {
|
||||||
|
old_path: old_path.to_path_buf(),
|
||||||
|
new_path,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Umbenennungen sequenziell ausführen
|
||||||
|
for op in rename_ops {
|
||||||
if let Some(bar) = &progress_bar {
|
if let Some(bar) = &progress_bar {
|
||||||
bar.inc(1);
|
bar.inc(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let old_path = entry.path();
|
if !args.quiet {
|
||||||
|
info!("{} -> {}", op.old_path.display(), op.new_path.display());
|
||||||
// Ebenentiefe 0 -> überspringen wir als Verzeichnis, außer --modify_root ist gesetzt
|
|
||||||
if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
|
|
||||||
if args.verbose {
|
|
||||||
debug!("Skip root directory: {}", old_path.display());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special Files (Symlinks, etc.) nur mit --special verarbeiten
|
if args.verbose {
|
||||||
let file_type = entry.file_type();
|
debug!("Rename: {:?} -> {:?}", op.old_path, op.new_path);
|
||||||
if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
|
|
||||||
if args.verbose {
|
|
||||||
debug!("Skip special file: {}", old_path.display());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dateiname (oder Verzeichnisname) ermitteln
|
if !args.dry_run && is_safe_rename(&op.old_path, &op.new_path, args.force) {
|
||||||
let filename = old_path.file_name().ok_or_else(|| {
|
fs::rename(&op.old_path, &op.new_path).with_context(|| {
|
||||||
anyhow::anyhow!(
|
format!(
|
||||||
"Konnte Dateinamen nicht ermitteln für: {}",
|
"Fehler beim Umbenennen: {} -> {}",
|
||||||
old_path.display()
|
op.old_path.display(),
|
||||||
)
|
op.new_path.display()
|
||||||
})?;
|
)
|
||||||
|
})?;
|
||||||
// Verarbeiten und ggf. umbenennen
|
|
||||||
if let Some(new_name) = clean_filename(filename, &config, args.verbose) {
|
|
||||||
let new_path = old_path.with_file_name(&new_name);
|
|
||||||
|
|
||||||
if !args.quiet {
|
|
||||||
info!("{} -> {}", old_path.display(), new_path.display());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !args.dry_run && is_safe_rename(old_path, &new_path, args.force) {
|
|
||||||
fs::rename(old_path, &new_path).with_context(|| {
|
|
||||||
format!(
|
|
||||||
"Fehler beim Umbenennen: {} -> {}",
|
|
||||||
old_path.display(),
|
|
||||||
new_path.display()
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue