Skip to content

Commit e4bf657

Browse files
committed
uucore: format: Small optimizations in num_format for seq
In most common use cases: - We can bypass a lot of `write_output` when width == 0. - Simplify format_float_decimal when the input is an integer. Also document interesting cases in src/uu/seq/BENCHMARKING.md.
1 parent 1db680a commit e4bf657

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

src/uu/seq/BENCHMARKING.md

+32-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,38 @@ Finally, you can compare the performance of the two versions of `seq`
1919
by running, for example,
2020

2121
```shell
22-
hyperfine "seq 1000000" "target/release/seq 1000000"
22+
hyperfine -L seq seq,target/release/seq "{seq} 1000000"
23+
```
24+
25+
## Interesting test cases
26+
27+
Performance characteristics may vary a lot depending on the parameters,
28+
and if custom formatting is required. In particular, it does appear
29+
that the GNU implementation is heavily optimized for positive integer
30+
outputs (which is probably the most common use case for `seq`).
31+
32+
Specifying a format or fixed width will slow down the
33+
execution a lot (~15-20 times on GNU `seq`):
34+
```shell
35+
hyperfine -L seq seq,target/release/seq "{seq} -f%g 1000000"
36+
hyperfine -L seq seq,target/release/seq "{seq} -w 1000000"
37+
```
38+
39+
Floating point increments, or any negative bound, also degrades the
40+
performance (~10-15 times on GNU `seq`):
41+
```shell
42+
hyperfine -L seq seq,./target/release/seq "{seq} 0 0.000001 1"
43+
hyperfine -L seq seq,./target/release/seq "{seq} -100 1 1000000"
44+
```
45+
46+
It is also interesting to compare performance with large precision
47+
format. But in this case, the output itself should also be compared,
48+
as GNU `seq` may not provide the same precision (`uutils` version of
49+
`seq` provides arbitrary precision, while GNU `seq` appears to be
50+
limited to `long double` on the given platform, i.e. 64/80/128-bit
51+
float):
52+
```shell
53+
hyperfine -L seq seq,target/release/seq "{seq} -f%.30f 0 0.000001 1"
2354
```
2455

2556
[0]: https://github.com/sharkdp/hyperfine

src/uucore/src/lib/features/format/num_format.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,16 @@ fn format_float_non_finite(e: &ExtendedBigDecimal, case: Case) -> String {
354354

355355
fn format_float_decimal(bd: &BigDecimal, precision: usize, force_decimal: ForceDecimal) -> String {
356356
debug_assert!(!bd.is_negative());
357-
if precision == 0 && force_decimal == ForceDecimal::Yes {
358-
format!("{bd:.0}.")
359-
} else {
360-
format!("{bd:.precision$}")
357+
if precision == 0 {
358+
let (bi, scale) = bd.as_bigint_and_scale();
359+
if scale == 0 && force_decimal != ForceDecimal::Yes {
360+
// Optimization when printing integers.
361+
return bi.to_str_radix(10);
362+
} else if force_decimal == ForceDecimal::Yes {
363+
return format!("{bd:.0}.");
364+
}
361365
}
366+
format!("{bd:.precision$}")
362367
}
363368

364369
fn format_float_scientific(
@@ -614,6 +619,11 @@ fn write_output(
614619
width: usize,
615620
alignment: NumberAlignment,
616621
) -> std::io::Result<()> {
622+
if width == 0 {
623+
writer.write_all(sign_indicator.as_bytes())?;
624+
writer.write_all(s.as_bytes())?;
625+
return Ok(());
626+
}
617627
// Take length of `sign_indicator`, which could be 0 or 1, into consideration when padding
618628
// by storing remaining_width indicating the actual width needed.
619629
// Using min() because self.width could be 0, 0usize - 1usize should be avoided

0 commit comments

Comments
 (0)