pr: stop dropping lines the columns do not divide evenly - #14088
pr: stop dropping lines the columns do not divide evenly#14088luantaraschi wants to merge 1 commit into
Conversation
to_table_short_file built the table with lines.len() / columns rows, so a page whose line count is not a multiple of the column count lost the remainder. `seq 9 | pr -2` dropped the ninth line, and a page holding fewer lines than it has columns came out empty. Spread the lines the way GNU pr does instead: every column takes the ceiling of what is still unplaced over the number of columns left to fill, which leaves nothing over and keeps the longer columns on the left. Two things followed from that. The short-file branch was tested before the across-mode branch, so `pr -a` read a partly filled page downwards, and the last row of such a page needs its line terminator without a trailing column separator.
Merging this PR will degrade performance by 0.4%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | du_summarize_balanced_tree[(5, 4, 10)] |
16 ms | 16.8 ms | -4.69% |
| ❌ | Simulation | du_wide_tree[(5000, 500)] |
19.4 ms | 20.3 ms | -4.1% |
| ⚡ | Simulation | cksum_crc32b |
39.7 ms | 38.1 ms | +4.19% |
| ⚡ | Simulation | cksum_sysv |
68.4 ms | 66.2 ms | +3.32% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing luantaraschi:fix/pr-column-partial-page (2be25be) with main (9875296)
Footnotes
-
50 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
GNU testsuite comparison: |
prin column mode drops whatever the columns cannot divide evenly.The ninth line is gone. A page holding fewer lines than it has columns comes out empty instead:
seq 9 | pr -2 -l 12ends the same way, on an empty page 3 where GNU prints the ninth line.It comes down to one line,
src/uu/pr/src/pr.rs:1408:Integer division truncates, so nine lines over two columns give four rows and the ninth never gets a cell.
Rounding up gets closer but still misses, because GNU balances the columns: each one takes the ceiling of what is still unplaced over the number of columns left to fill, so nine lines over four columns come out 3, 2, 2, 2 rather than 3, 3, 3, 0. I read that rule off the output of
/usr/bin/prrather than its source, then checked it for every column count from 1 to 7 against every input length from 0 to 39, down and across. 560 cases, no mismatch.Two smaller things came with the fix. The short-file branch was tested before the across-mode one, so
pr -alaid a partly filled page out downwards; and the last row of such a page stops part way through, so it needs its line separator without a trailing column separator.What I ran
All of it in a Debian container where
/usr/bin/pris GNU coreutils 9.7.I ran 425 combinations of options and input length against GNU, this branch, and a build of
main.mainagreed with GNU in 138 of them and this branch agrees in 370, so 232 newly agree and none regressed. The comparison ignores column padding: uutils does not match GNU there, andtest_columnsalready carries a TODO about it.Another 574 combinations went through a line count check, comparing how many input lines survive the round trip. None lost.
cargo test --features pr --no-default-features test_prpasses 83. The four new tests fail onmainand pass here, the 79 that were already there are untouched, and no expected fixture changed.cargo fmt -p uu_pr -- --checkandcargo clippy -p uu_pr --all-targetscome back clean.clippy -D warningsstill trips on the generateduucore/embedded_locales.rs, which fails the same way for a crate I did not touch.I left the padding alone. GNU pads with tabs and leaves the last column unpadded while uutils pads with spaces, which is a different problem.