diff --git a/src/uu/seq/BENCHMARKING.md b/src/uu/seq/BENCHMARKING.md index a633d509c3b..89cc4786170 100644 --- a/src/uu/seq/BENCHMARKING.md +++ b/src/uu/seq/BENCHMARKING.md @@ -19,7 +19,38 @@ Finally, you can compare the performance of the two versions of `seq` by running, for example, ```shell -hyperfine "seq 1000000" "target/release/seq 1000000" +hyperfine -L seq seq,target/release/seq "{seq} 1000000" ``` +## Interesting test cases + +Performance characteristics may vary a lot depending on the parameters, +and if custom formatting is required. In particular, it does appear +that the GNU implementation is heavily optimized for positive integer +outputs (which is probably the most common use case for `seq`). + +Specifying a format or fixed width will slow down the +execution a lot (~15-20 times on GNU `seq`): +```shell +hyperfine -L seq seq,target/release/seq "{seq} -f%g 1000000" +hyperfine -L seq seq,target/release/seq "{seq} -w 1000000" +``` + +Floating point increments, or any negative bound, also degrades the +performance (~10-15 times on GNU `seq`): +```shell +hyperfine -L seq seq,./target/release/seq "{seq} 0 0.000001 1" +hyperfine -L seq seq,./target/release/seq "{seq} -100 1 1000000" +``` + +## Optimizations + +### Buffering stdout + +The original `uutils` implementation of `seq` did unbuffered writes +to stdout, causing a large number of system calls (and therefore a large amount +of system time). Simply wrapping `stdout` in a `BufWriter` increased performance +by about 2 times for a floating point increment test case, leading to similar +performance compared with GNU `seq`. + [0]: https://github.com/sharkdp/hyperfine diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index a6b5e32ea84..08b989815bf 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. // spell-checker:ignore (ToDO) bigdecimal extendedbigdecimal numberparse hexadecimalfloat use std::ffi::OsString; -use std::io::{stdout, ErrorKind, Write}; +use std::io::{stdout, BufWriter, ErrorKind, Write}; use clap::{Arg, ArgAction, Command}; use num_traits::{ToPrimitive, Zero}; @@ -262,8 +262,8 @@ fn print_seq( padding: usize, format: Option<&Format>, ) -> std::io::Result<()> { - let stdout = stdout(); - let mut stdout = stdout.lock(); + let stdout = stdout().lock(); + let mut stdout = BufWriter::new(stdout); let (first, increment, last) = range; let mut value = first; let padding = if pad {