Add option to disable parallel file processing (#695)

Fixes #685.
Fixes #572.
Also fixes #690 by using "logical CPUs".
This commit is contained in:
andrews05 2025-04-23 21:09:15 +12:00 committed by GitHub
parent d168fffb12
commit a44ba5f88b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 28 deletions

View file

@ -350,16 +350,31 @@ timeout before each transformation or compression trial, and will stop trying to
the file if the timeout is exceeded. Note that this does not cut short any operations that \ the file if the timeout is exceeded. Note that this does not cut short any operations that \
are already in progress, so it is currently of limited effectiveness for large files with \ are already in progress, so it is currently of limited effectiveness for large files with \
high compression levels.") high compression levels.")
.value_name("secs")
.long("timeout") .long("timeout")
.value_name("secs")
.value_parser(value_parser!(u64)), .value_parser(value_parser!(u64)),
) )
.arg( .arg(
Arg::new("threads") Arg::new("threads")
.help("Set number of threads to use [default: num CPU cores]") .help("Number of threads to use [default: num logical CPUs]")
.long("threads") .long_help("\
Set the maximum number of threads to use. Oxipng uses multithreading to evaluate multiple \
optimizations on the same file in parallel as well as process multiple files in parallel. \
You can set this to a lower value if you need to limit memory or CPU usage.
[default: num logical CPUs]")
.short('t') .short('t')
.long("threads")
.value_name("num") .value_name("num")
.value_parser(value_parser!(usize)), .value_parser(value_parser!(usize)),
) )
.arg(
Arg::new("parallel-files")
.help("Process multiple files sequentially")
.long_help("\
Process multiple files sequentially rather than in parallel. Use this if you need \
determinism in the processing order. Note this is not necessary if using '--threads 1'.")
.long("sequential")
.action(ArgAction::SetFalse),
)
} }

View file

@ -72,32 +72,20 @@ fn main() -> ExitCode {
true, true,
); );
let summary = files let parallel_files = matches.get_flag("parallel-files");
.into_par_iter() let summary = if parallel_files {
.map(|(input, output)| { files
match oxipng::optimize(&input, &output, &opts) { .into_par_iter()
// For optimizing single files, this will return the correct exit code always. .map(|(input, output)| process_file(&input, &output, &opts))
// For recursive optimization, the correct choice is a bit subjective. .min()
// We're choosing to return a 0 exit code if ANY file in the set } else {
// runs correctly. files
// The reason for this is that recursion may pick up files that are not .into_iter()
// PNG files, and return an error for them. .map(|(input, output)| process_file(&input, &output, &opts))
// We don't really want to return an error code for those files. .min()
Ok(_) => OptimizationResult::Ok, };
Err(e @ PngError::C2PAMetadataPreventsChanges) => {
warn!("{input}: {e}");
OptimizationResult::Skipped
}
Err(e) => {
error!("{input}: {e}");
OptimizationResult::Failed
}
}
})
.min()
.unwrap_or(OptimizationResult::Skipped);
match summary { match summary.unwrap_or(OptimizationResult::Skipped) {
OptimizationResult::Ok => ExitCode::SUCCESS, OptimizationResult::Ok => ExitCode::SUCCESS,
OptimizationResult::Failed => ExitCode::FAILURE, OptimizationResult::Failed => ExitCode::FAILURE,
OptimizationResult::Skipped => ExitCode::from(3), OptimizationResult::Skipped => ExitCode::from(3),
@ -421,3 +409,24 @@ fn parse_numeric_range_opts(
Err(ERROR_MESSAGE.to_owned()) Err(ERROR_MESSAGE.to_owned())
} }
fn process_file(input: &InFile, output: &OutFile, opts: &Options) -> OptimizationResult {
match oxipng::optimize(input, output, opts) {
// For optimizing single files, this will return the correct exit code always.
// For recursive optimization, the correct choice is a bit subjective.
// We're choosing to return a 0 exit code if ANY file in the set
// runs correctly.
// The reason for this is that recursion may pick up files that are not
// PNG files, and return an error for them.
// We don't really want to return an error code for those files.
Ok(_) => OptimizationResult::Ok,
Err(e @ PngError::C2PAMetadataPreventsChanges) => {
warn!("{input}: {e}");
OptimizationResult::Skipped
}
Err(e) => {
error!("{input}: {e}");
OptimizationResult::Failed
}
}
}