1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 20:17:45 +00:00

seq: Add constant width support in fast path

It is actually quite easy to implement, we just start with
a padded number and increment as usual.
This commit is contained in:
Nicolas Boichat 2025-04-03 18:47:45 +02:00
parent 560d1eb1b7
commit c311e208ae
2 changed files with 28 additions and 14 deletions

View file

@ -88,7 +88,8 @@ with the default implementation, at the expense of some added code complexity.
Just from performance numbers, it is clear that GNU `seq` uses similar Just from performance numbers, it is clear that GNU `seq` uses similar
tricks, but we are more liberal on when we use our fast path (e.g. large tricks, but we are more liberal on when we use our fast path (e.g. large
increments are supported). Our fast path implementation gets within ~10% increments are supported, equal width is supported). Our fast path
of `seq` performance. implementation gets within ~10% of `seq` performance when its fast
path is activated.
[0]: https://github.com/sharkdp/hyperfine [0]: https://github.com/sharkdp/hyperfine

View file

@ -151,13 +151,17 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
} }
}; };
let precision = select_precision(&first, &increment, &last);
// If a format was passed on the command line, use that. // If a format was passed on the command line, use that.
// If not, use some default format based on parameters precision. // If not, use some default format based on parameters precision.
let format = match options.format { let (format, padding, fast_allowed) = match options.format {
Some(str) => Format::<num_format::Float, &ExtendedBigDecimal>::parse(str)?, Some(str) => (
Format::<num_format::Float, &ExtendedBigDecimal>::parse(str)?,
0,
false,
),
None => { None => {
let precision = select_precision(&first, &increment, &last);
let padding = if options.equal_width { let padding = if options.equal_width {
let precision_value = precision.unwrap_or(0); let precision_value = precision.unwrap_or(0);
first first
@ -188,19 +192,22 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
..Default::default() ..Default::default()
}, },
}; };
Format::from_formatter(formatter) // Allow fast printing if precision is 0 (integer inputs), `print_seq` will do further checks.
(
Format::from_formatter(formatter),
padding,
precision == Some(0),
)
} }
}; };
// Allow fast printing, `print_seq` will do further checks.
let fast_allowed = options.format.is_none() && !options.equal_width && precision == Some(0);
let result = print_seq( let result = print_seq(
(first.number, increment.number, last.number), (first.number, increment.number, last.number),
&options.separator, &options.separator,
&options.terminator, &options.terminator,
&format, &format,
fast_allowed, fast_allowed,
padding,
); );
match result { match result {
@ -304,8 +311,6 @@ fn fast_inc(val: &mut [u8], start: usize, end: usize, inc: &[u8]) -> usize {
/// Integer print, default format, positive increment: fast code path /// Integer print, default format, positive increment: fast code path
/// that avoids reformating digit at all iterations. /// that avoids reformating digit at all iterations.
/// TODO: We could easily support equal_width (we do quite a bit of work
/// _not_ supporting that and aligning the number to the left).
fn fast_print_seq( fn fast_print_seq(
mut stdout: impl Write, mut stdout: impl Write,
first: &BigUint, first: &BigUint,
@ -313,6 +318,7 @@ fn fast_print_seq(
last: &BigUint, last: &BigUint,
separator: &str, separator: &str,
terminator: &str, terminator: &str,
padding: usize,
) -> std::io::Result<()> { ) -> std::io::Result<()> {
// Nothing to do, just return. // Nothing to do, just return.
if last < first { if last < first {
@ -338,8 +344,9 @@ fn fast_print_seq(
// //
// We keep track of start in this buffer, as the number grows. // We keep track of start in this buffer, as the number grows.
// When printing, we take a slice between start and end. // When printing, we take a slice between start and end.
let size = separator.len() + last_length; let size = last_length.max(padding) + separator.len();
let mut buf = vec![0u8; size]; // Fill with '0', this is needed for equal_width, and harmless otherwise.
let mut buf = vec![b'0'; size];
let buf = buf.as_mut_slice(); let buf = buf.as_mut_slice();
let num_end = buf.len() - separator.len(); let num_end = buf.len() - separator.len();
@ -349,6 +356,10 @@ fn fast_print_seq(
buf[start..num_end].copy_from_slice(first_str.as_bytes()); buf[start..num_end].copy_from_slice(first_str.as_bytes());
buf[num_end..].copy_from_slice(separator.as_bytes()); buf[num_end..].copy_from_slice(separator.as_bytes());
// Normally, if padding is > 0, it should be equal to last_length,
// so start would be == 0, but there are corner cases.
start = start.min(num_end - padding);
// Prepare the number to increment with as a string // Prepare the number to increment with as a string
let inc_str = increment.to_string(); let inc_str = increment.to_string();
let inc_str = inc_str.as_bytes(); let inc_str = inc_str.as_bytes();
@ -379,6 +390,7 @@ fn print_seq(
terminator: &str, terminator: &str,
format: &Format<num_format::Float, &ExtendedBigDecimal>, format: &Format<num_format::Float, &ExtendedBigDecimal>,
fast_allowed: bool, fast_allowed: bool,
padding: usize, // Used by fast path only
) -> std::io::Result<()> { ) -> std::io::Result<()> {
let stdout = stdout().lock(); let stdout = stdout().lock();
let mut stdout = BufWriter::new(stdout); let mut stdout = BufWriter::new(stdout);
@ -402,6 +414,7 @@ fn print_seq(
&last_bui, &last_bui,
separator, separator,
terminator, terminator,
padding,
); );
} }
} }