Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cf8ebf5

Browse files
committed
cmp: completely avoid Rust fmt in verbose mode
This makes the code less readable, but gets us a massive improvement to performance. Comparing ~36M completely different files now takes ~40% of the time. Compared to GNU cmp, we now run the same comparison in ~26% of the time. This also improves comparing binary files. A comparison of chromium and libxul now takes ~60% of the time. We also beat GNU cmpi by about the same margin. Before: > hyperfine --warmup 1 -i --output=pipe \ '../target/release/diffutils cmp -l huge huge.3' Benchmark 1: ../target/release/diffutils cmp -l huge huge.3 Time (mean ± σ): 2.000 s ± 0.016 s [User: 1.603 s, System: 0.392 s] Range (min … max): 1.989 s … 2.043 s 10 runs Warning: Ignoring non-zero exit code. > hyperfine --warmup 1 -i --output=pipe \ '../target/release/diffutils cmp -l -b \ /usr/lib64/chromium-browser/chromium-browser \ /usr/lib64/firefox/libxul.so' Benchmark 1: ../target/release/diffutils cmp -l -b /usr/lib64/chromium-browser/chromium-browser /usr/lib64/firefox/libxul.so Time (mean ± σ): 24.704 s ± 0.162 s [User: 21.948 s, System: 2.700 s] Range (min … max): 24.359 s … 24.889 s 10 runs Warning: Ignoring non-zero exit code. After: > hyperfine --warmup 1 -i --output=pipe \ '../target/release/diffutils cmp -l huge huge.3' Benchmark 1: ../target/release/diffutils cmp -l huge huge.3 Time (mean ± σ): 849.5 ms ± 6.2 ms [User: 538.3 ms, System: 306.8 ms] Range (min … max): 839.4 ms … 857.7 ms 10 runs Warning: Ignoring non-zero exit code. > hyperfine --warmup 1 -i --output=pipe \ '../target/release/diffutils cmp -l -b \ /usr/lib64/chromium-browser/chromium-browser \ /usr/lib64/firefox/libxul.so' Benchmark 1: ../target/release/diffutils cmp -l -b /usr/lib64/chromium-browser/chromium-browser /usr/lib64/firefox/libxul.so Time (mean ± σ): 14.646 s ± 0.040 s [User: 12.328 s, System: 2.286 s] Range (min … max): 14.585 s … 14.702 s 10 runs Warning: Ignoring non-zero exit code.
1 parent cfd8f8f commit cf8ebf5

File tree

1 file changed

+65
-19
lines changed

1 file changed

+65
-19
lines changed

src/cmp.rs

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ fn format_byte(byte: u8) -> String {
475475
unsafe { String::from_utf8_unchecked(quoted) }
476476
}
477477

478+
// This function has been optimized to not use the Rust fmt system, which
479+
// leads to a massive speed up when processing large files: cuts the time
480+
// for comparing 2 ~36MB completely different files in half on an M1 Max.
478481
fn report_verbose_diffs(diffs: Vec<(usize, u8, u8)>, params: &Params) -> Result<(), String> {
479482
assert!(!params.quiet);
480483

@@ -487,19 +490,49 @@ fn report_verbose_diffs(diffs: Vec<(usize, u8, u8)>, params: &Params) -> Result<
487490
let mut from_oct = [0u8; 3]; // for octal conversions
488491
let mut to_oct = [0u8; 3];
489492

493+
// Capacity calc: at_byte width + 2 x 3-byte octal numbers + 4-byte value + up to 2 byte value + 4 spaces
494+
let mut output = Vec::<u8>::with_capacity(width + 3 * 2 + 4 + 2 + 4);
495+
490496
if params.print_bytes {
491497
for (at_byte, from_byte, to_byte) in diffs {
498+
output.clear();
499+
500+
// "{:>width$} {:>3o} {:4} {:>3o} {}",
492501
let at_byte_str = at_byte_buf.format(at_byte);
493-
writeln!(
494-
stdout,
495-
"{:>width$} {} {:4} {} {}",
496-
at_byte_str,
497-
format_octal(from_byte, &mut from_oct),
498-
format_byte(from_byte),
499-
format_octal(to_byte, &mut to_oct),
500-
format_byte(to_byte),
501-
)
502-
.map_err(|e| {
502+
let at_byte_padding = width - at_byte_str.len();
503+
504+
for _ in 0..at_byte_padding {
505+
output.push(b' ')
506+
}
507+
508+
output.extend_from_slice(at_byte_str.as_bytes());
509+
510+
output.push(b' ');
511+
512+
output.extend_from_slice(format_octal(from_byte, &mut from_oct).as_bytes());
513+
514+
output.push(b' ');
515+
516+
let from_byte_str = format_byte(from_byte);
517+
let from_byte_padding = 4 - from_byte_str.len();
518+
519+
output.extend_from_slice(from_byte_str.as_bytes());
520+
521+
for _ in 0..from_byte_padding {
522+
output.push(b' ')
523+
}
524+
525+
output.push(b' ');
526+
527+
output.extend_from_slice(format_octal(to_byte, &mut to_oct).as_bytes());
528+
529+
output.push(b' ');
530+
531+
output.extend_from_slice(format_byte(to_byte).as_bytes());
532+
533+
output.push(b'\n');
534+
535+
stdout.write_all(output.as_slice()).map_err(|e| {
503536
format!(
504537
"{}: error printing output: {e}",
505538
params.executable.to_string_lossy()
@@ -508,16 +541,29 @@ fn report_verbose_diffs(diffs: Vec<(usize, u8, u8)>, params: &Params) -> Result<
508541
}
509542
} else {
510543
for (at_byte, from_byte, to_byte) in diffs {
544+
output.clear();
545+
546+
// "{:>width$} {:>3o} {:>3o}"
511547
let at_byte_str = at_byte_buf.format(at_byte);
512-
writeln!(
513-
stdout,
514-
"{:>width$} {} {}",
515-
at_byte_str,
516-
format_octal(from_byte, &mut from_oct),
517-
format_octal(to_byte, &mut to_oct),
518-
width = width
519-
)
520-
.map_err(|e| {
548+
let at_byte_padding = width - at_byte_str.len();
549+
550+
for _ in 0..at_byte_padding {
551+
output.push(b' ')
552+
}
553+
554+
output.extend_from_slice(at_byte_str.as_bytes());
555+
556+
output.push(b' ');
557+
558+
output.extend_from_slice(format_octal(from_byte, &mut from_oct).as_bytes());
559+
560+
output.push(b' ');
561+
562+
output.extend_from_slice(format_octal(to_byte, &mut to_oct).as_bytes());
563+
564+
output.push(b'\n');
565+
566+
stdout.write_all(output.as_slice()).map_err(|e| {
521567
format!(
522568
"{}: error printing output: {e}",
523569
params.executable.to_string_lossy()

0 commit comments

Comments
 (0)