Skip to content

gh-130167: Minor textwrap.dedent() optimization #131925

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from

Conversation

Marius-Juston
Copy link
Contributor

@Marius-Juston Marius-Juston commented Mar 31, 2025

Minor optimization to @AA-Turner #131792 where you compute the min and max of the non_blank_lines simultaneously, removes additional use of line.isspace() as well.

Benchmark raw new
raw_text: "abc \t" 2.91 ms 3.33 ms: 1.15x slower
raw_text: " " 4.31 ms 4.17 ms: 1.03x faster
raw_text: " \t abc" 4.04 ms 4.40 ms: 1.09x slower
raw_text: " \t abc \t " 4.08 ms 4.40 ms: 1.08x slower
raw_text: 1000 spaces 34.9 ms 27.9 ms: 1.25x faster
Basic indented text with empty lines 1.88 us 1.82 us: 1.03x faster
Text with mixed indentation and blank lines 1.85 us 1.79 us: 1.03x faster
No indentation (edge case) 1.24 us 1.14 us: 1.09x faster
Only blank lines 958 ns 641 ns: 1.49x faster
Edge case: No common prefix to remove 1.09 us 993 ns: 1.10x faster
Edge case: Single indented line 998 ns 855 ns: 1.17x faster
Edge case: Single indented line only 631 ns 432 ns: 1.46x faster
Edge case: Empty text 89.3 ns 81.0 ns: 1.10x faster
no_indent 2.57 us 2.50 us: 1.03x faster
mixed 10.4 us 10.9 us: 1.05x slower
large_text 77.7 us 85.7 us: 1.10x slower
whitespace_only 859 ns 599 ns: 1.43x faster
Geometric mean (ref) 1.08x faster

@ghost
Copy link

ghost commented Mar 31, 2025

All commit authors signed the Contributor License Agreement.
CLA signed

@AA-Turner
Copy link
Member

It seems for the benchmarks handling large text, there's a consistent slowdown. Could you plot a chart of running the function with inputs of different sizes? Perhaps take the unicodeobject file and profile the first 500 lines, then the first 1000, 1500, etc, for this PR and the version using min/max.

A

@Marius-Juston
Copy link
Contributor Author

It stays a consistently slower by 5-8% except for long lines test

Benchmark raw_scale new_scale
lines: 500 raw_text: no prefix 93.0 us 99.2 us: 1.07x slower
lines: 1000 raw_text: no prefix 193 us 203 us: 1.05x slower
lines: 1500 raw_text: no prefix 289 us 306 us: 1.06x slower
lines: 2000 raw_text: no prefix 387 us 415 us: 1.07x slower
lines: 2500 raw_text: no prefix 493 us 521 us: 1.06x slower
lines: 3000 raw_text: no prefix 593 us 639 us: 1.08x slower
lines: 3500 raw_text: no prefix 697 us 738 us: 1.06x slower
lines: 4000 raw_text: no prefix 799 us 841 us: 1.05x slower
lines: 4500 raw_text: no prefix 895 us 952 us: 1.06x slower
lines: 5000 raw_text: no prefix 1.01 ms 1.07 ms: 1.06x slower
lines: 5500 raw_text: no prefix 1.10 ms 1.18 ms: 1.07x slower
lines: 6000 raw_text: no prefix 1.21 ms 1.29 ms: 1.07x slower
lines: 6500 raw_text: no prefix 1.31 ms 1.39 ms: 1.06x slower
lines: 7000 raw_text: no prefix 1.43 ms 1.52 ms: 1.07x slower
lines: 7500 raw_text: no prefix 1.52 ms 1.61 ms: 1.06x slower
lines: 8000 raw_text: no prefix 1.63 ms 1.73 ms: 1.06x slower
lines: 8500 raw_text: no prefix 1.71 ms 1.81 ms: 1.06x slower
lines: 9000 raw_text: no prefix 1.82 ms 1.92 ms: 1.05x slower
lines: 9500 raw_text: no prefix 1.92 ms 2.04 ms: 1.06x slower
lines: 500 raw_text: "abc \t" 89.7 us 99.2 us: 1.11x slower
lines: 1000 raw_text: "abc \t" 175 us 201 us: 1.15x slower
lines: 1500 raw_text: "abc \t" 265 us 300 us: 1.13x slower
lines: 2000 raw_text: "abc \t" 354 us 404 us: 1.14x slower
lines: 2500 raw_text: "abc \t" 446 us 523 us: 1.17x slower
lines: 3000 raw_text: "abc \t" 537 us 609 us: 1.14x slower
lines: 3500 raw_text: "abc \t" 628 us 708 us: 1.13x slower
lines: 4000 raw_text: "abc \t" 718 us 813 us: 1.13x slower
lines: 4500 raw_text: "abc \t" 803 us 920 us: 1.15x slower
lines: 5000 raw_text: "abc \t" 908 us 1.02 ms: 1.13x slower
lines: 5500 raw_text: "abc \t" 988 us 1.13 ms: 1.14x slower
lines: 6000 raw_text: "abc \t" 1.09 ms 1.26 ms: 1.16x slower
lines: 6500 raw_text: "abc \t" 1.18 ms 1.33 ms: 1.13x slower
lines: 7000 raw_text: "abc \t" 1.28 ms 1.44 ms: 1.12x slower
lines: 7500 raw_text: "abc \t" 1.37 ms 1.54 ms: 1.13x slower
lines: 8000 raw_text: "abc \t" 1.47 ms 1.63 ms: 1.11x slower
lines: 8500 raw_text: "abc \t" 1.54 ms 1.75 ms: 1.14x slower
lines: 9000 raw_text: "abc \t" 1.64 ms 1.88 ms: 1.15x slower
lines: 9500 raw_text: "abc \t" 1.72 ms 1.93 ms: 1.12x slower
lines: 500 raw_text: " " 104 us 108 us: 1.04x slower
lines: 1000 raw_text: " " 213 us 221 us: 1.04x slower
lines: 1500 raw_text: " " 320 us 336 us: 1.05x slower
lines: 2000 raw_text: " " 435 us 473 us: 1.09x slower
lines: 2500 raw_text: " " 544 us 563 us: 1.04x slower
lines: 3000 raw_text: " " 658 us 684 us: 1.04x slower
lines: 3500 raw_text: " " 767 us 798 us: 1.04x slower
lines: 4000 raw_text: " " 884 us 916 us: 1.04x slower
lines: 4500 raw_text: " " 995 us 1.05 ms: 1.05x slower
lines: 5000 raw_text: " " 1.11 ms 1.22 ms: 1.10x slower
lines: 5500 raw_text: " " 1.22 ms 1.28 ms: 1.05x slower
lines: 6000 raw_text: " " 1.34 ms 1.40 ms: 1.04x slower
lines: 6500 raw_text: " " 1.46 ms 1.51 ms: 1.04x slower
lines: 7000 raw_text: " " 1.57 ms 1.63 ms: 1.04x slower
lines: 7500 raw_text: " " 1.68 ms 1.74 ms: 1.04x slower
lines: 8000 raw_text: " " 1.79 ms 1.86 ms: 1.04x slower
lines: 8500 raw_text: " " 1.93 ms 1.98 ms: 1.03x slower
lines: 9000 raw_text: " " 2.02 ms 2.08 ms: 1.03x slower
lines: 9500 raw_text: " " 2.12 ms 2.21 ms: 1.04x slower
lines: 500 raw_text: "\t" 101 us 106 us: 1.05x slower
lines: 1000 raw_text: "\t" 206 us 217 us: 1.05x slower
lines: 1500 raw_text: "\t" 318 us 331 us: 1.04x slower
lines: 2000 raw_text: "\t" 427 us 443 us: 1.04x slower
lines: 2500 raw_text: "\t" 537 us 569 us: 1.06x slower
lines: 3000 raw_text: "\t" 644 us 677 us: 1.05x slower
lines: 3500 raw_text: "\t" 758 us 790 us: 1.04x slower
lines: 4000 raw_text: "\t" 866 us 908 us: 1.05x slower
lines: 4500 raw_text: "\t" 981 us 1.03 ms: 1.05x slower
lines: 5000 raw_text: "\t" 1.10 ms 1.15 ms: 1.05x slower
lines: 5500 raw_text: "\t" 1.20 ms 1.26 ms: 1.05x slower
lines: 6000 raw_text: "\t" 1.32 ms 1.38 ms: 1.04x slower
lines: 6500 raw_text: "\t" 1.43 ms 1.50 ms: 1.06x slower
lines: 7000 raw_text: "\t" 1.55 ms 1.62 ms: 1.05x slower
lines: 7500 raw_text: "\t" 1.66 ms 1.72 ms: 1.04x slower
lines: 8000 raw_text: "\t" 1.79 ms 1.84 ms: 1.03x slower
lines: 8500 raw_text: "\t" 1.91 ms 1.97 ms: 1.03x slower
lines: 9000 raw_text: "\t" 1.98 ms 2.07 ms: 1.04x slower
lines: 9500 raw_text: "\t" 2.10 ms 2.19 ms: 1.04x slower
lines: 500 raw_text: " \t abc" 101 us 106 us: 1.05x slower
lines: 1000 raw_text: " \t abc" 198 us 214 us: 1.08x slower
lines: 1500 raw_text: " \t abc" 299 us 323 us: 1.08x slower
lines: 2000 raw_text: " \t abc" 404 us 436 us: 1.08x slower
lines: 2500 raw_text: " \t abc" 500 us 553 us: 1.11x slower
lines: 3000 raw_text: " \t abc" 611 us 658 us: 1.08x slower
lines: 3500 raw_text: " \t abc" 703 us 778 us: 1.11x slower
lines: 4000 raw_text: " \t abc" 806 us 880 us: 1.09x slower
lines: 4500 raw_text: " \t abc" 916 us 1.01 ms: 1.10x slower
lines: 5000 raw_text: " \t abc" 1.01 ms 1.12 ms: 1.10x slower
lines: 5500 raw_text: " \t abc" 1.12 ms 1.23 ms: 1.10x slower
lines: 6000 raw_text: " \t abc" 1.22 ms 1.35 ms: 1.10x slower
lines: 6500 raw_text: " \t abc" 1.33 ms 1.47 ms: 1.11x slower
lines: 7000 raw_text: " \t abc" 1.43 ms 1.56 ms: 1.09x slower
lines: 7500 raw_text: " \t abc" 1.52 ms 1.67 ms: 1.10x slower
lines: 8000 raw_text: " \t abc" 1.64 ms 1.77 ms: 1.08x slower
lines: 8500 raw_text: " \t abc" 1.72 ms 1.88 ms: 1.10x slower
lines: 9000 raw_text: " \t abc" 1.83 ms 2.00 ms: 1.09x slower
lines: 9500 raw_text: " \t abc" 2.28 ms 2.49 ms: 1.09x slower
lines: 500 raw_text: " \t abc \t " 102 us 115 us: 1.13x slower
lines: 1000 raw_text: " \t abc \t " 199 us 228 us: 1.15x slower
lines: 1500 raw_text: " \t abc \t " 303 us 326 us: 1.07x slower
lines: 2000 raw_text: " \t abc \t " 400 us 438 us: 1.09x slower
lines: 2500 raw_text: " \t abc \t " 504 us 549 us: 1.09x slower
lines: 3000 raw_text: " \t abc \t " 611 us 659 us: 1.08x slower
lines: 3500 raw_text: " \t abc \t " 715 us 768 us: 1.07x slower
lines: 4000 raw_text: " \t abc \t " 816 us 884 us: 1.08x slower
lines: 4500 raw_text: " \t abc \t " 912 us 1.01 ms: 1.10x slower
lines: 5000 raw_text: " \t abc \t " 1.03 ms 1.12 ms: 1.09x slower
lines: 5500 raw_text: " \t abc \t " 1.13 ms 1.24 ms: 1.10x slower
lines: 6000 raw_text: " \t abc \t " 1.23 ms 1.35 ms: 1.09x slower
lines: 6500 raw_text: " \t abc \t " 1.33 ms 1.45 ms: 1.09x slower
lines: 7000 raw_text: " \t abc \t " 1.44 ms 1.56 ms: 1.08x slower
lines: 7500 raw_text: " \t abc \t " 1.54 ms 1.67 ms: 1.08x slower
lines: 8000 raw_text: " \t abc \t " 1.64 ms 1.78 ms: 1.09x slower
lines: 8500 raw_text: " \t abc \t " 1.75 ms 1.91 ms: 1.09x slower
lines: 9000 raw_text: " \t abc \t " 2.19 ms 2.30 ms: 1.05x slower
lines: 9500 raw_text: " \t abc \t " 2.33 ms 2.53 ms: 1.08x slower
lines: 500 raw_text: 1000 spaces 906 us 680 us: 1.33x faster
lines: 1000 raw_text: 1000 spaces 1.79 ms 1.31 ms: 1.36x faster
lines: 1500 raw_text: 1000 spaces 2.67 ms 1.95 ms: 1.37x faster
lines: 2000 raw_text: 1000 spaces 3.55 ms 2.62 ms: 1.36x faster
lines: 2500 raw_text: 1000 spaces 4.42 ms 3.25 ms: 1.36x faster
lines: 3000 raw_text: 1000 spaces 5.63 ms 3.88 ms: 1.45x faster
lines: 3500 raw_text: 1000 spaces 6.67 ms 4.51 ms: 1.48x faster
lines: 4000 raw_text: 1000 spaces 7.73 ms 5.60 ms: 1.38x faster
lines: 4500 raw_text: 1000 spaces 8.83 ms 6.46 ms: 1.37x faster
lines: 5000 raw_text: 1000 spaces 9.91 ms 7.28 ms: 1.36x faster
lines: 5500 raw_text: 1000 spaces 11.0 ms 8.12 ms: 1.35x faster
lines: 6000 raw_text: 1000 spaces 12.0 ms 9.02 ms: 1.34x faster
lines: 6500 raw_text: 1000 spaces 13.1 ms 9.76 ms: 1.34x faster
lines: 7000 raw_text: 1000 spaces 14.2 ms 10.7 ms: 1.33x faster
lines: 7500 raw_text: 1000 spaces 15.5 ms 11.5 ms: 1.35x faster
lines: 8000 raw_text: 1000 spaces 16.5 ms 12.3 ms: 1.34x faster
lines: 8500 raw_text: 1000 spaces 17.6 ms 13.4 ms: 1.31x faster
lines: 9000 raw_text: 1000 spaces 18.8 ms 14.1 ms: 1.33x faster
lines: 9500 raw_text: 1000 spaces 20.0 ms 15.0 ms: 1.33x faster
Geometric mean (ref) 1.02x slower

@Marius-Juston
Copy link
Contributor Author

From additional testing, this way is slightly faster in the ranges where the lines are between 1-18.

Benchmark raw_scale_2 new_scale_2
lines: 2 raw_text: no prefix 890 ns 764 ns: 1.16x faster
lines: 4 raw_text: no prefix 1.25 us 1.16 us: 1.08x faster
lines: 6 raw_text: no prefix 1.56 us 1.47 us: 1.06x faster
lines: 8 raw_text: no prefix 1.84 us 1.77 us: 1.04x faster
lines: 10 raw_text: no prefix 2.14 us 2.08 us: 1.03x faster
lines: 20 raw_text: no prefix 3.80 us 3.86 us: 1.02x slower
lines: 22 raw_text: no prefix 4.21 us 4.30 us: 1.02x slower
lines: 24 raw_text: no prefix 4.64 us 4.77 us: 1.03x slower
lines: 26 raw_text: no prefix 5.10 us 5.19 us: 1.02x slower
lines: 28 raw_text: no prefix 5.43 us 5.52 us: 1.02x slower
lines: 30 raw_text: no prefix 5.73 us 5.81 us: 1.01x slower
lines: 32 raw_text: no prefix 6.20 us 6.30 us: 1.02x slower
lines: 36 raw_text: no prefix 6.95 us 7.14 us: 1.03x slower
lines: 38 raw_text: no prefix 7.27 us 7.49 us: 1.03x slower
lines: 40 raw_text: no prefix 7.55 us 7.73 us: 1.02x slower
lines: 42 raw_text: no prefix 7.89 us 8.13 us: 1.03x slower
lines: 44 raw_text: no prefix 8.31 us 8.54 us: 1.03x slower
lines: 46 raw_text: no prefix 8.78 us 8.92 us: 1.02x slower
lines: 48 raw_text: no prefix 9.17 us 9.41 us: 1.03x slower
lines: 2 raw_text: "abc \t" 894 ns 765 ns: 1.17x faster
lines: 4 raw_text: "abc \t" 1.29 us 1.21 us: 1.07x faster
lines: 6 raw_text: "abc \t" 1.60 us 1.52 us: 1.05x faster
lines: 8 raw_text: "abc \t" 1.88 us 1.80 us: 1.04x faster
lines: 10 raw_text: "abc \t" 2.18 us 2.12 us: 1.03x faster
lines: 12 raw_text: "abc \t" 2.59 us 2.54 us: 1.02x faster
lines: 14 raw_text: "abc \t" 2.97 us 2.87 us: 1.03x faster
lines: 16 raw_text: "abc \t" 3.19 us 3.16 us: 1.01x faster
lines: 22 raw_text: "abc \t" 4.32 us 4.38 us: 1.01x slower
lines: 24 raw_text: "abc \t" 4.78 us 4.91 us: 1.03x slower
lines: 26 raw_text: "abc \t" 5.20 us 5.30 us: 1.02x slower
lines: 28 raw_text: "abc \t" 5.57 us 5.71 us: 1.03x slower
lines: 30 raw_text: "abc \t" 5.87 us 6.01 us: 1.02x slower
lines: 32 raw_text: "abc \t" 6.28 us 6.52 us: 1.04x slower
lines: 34 raw_text: "abc \t" 6.68 us 6.86 us: 1.03x slower
lines: 36 raw_text: "abc \t" 7.07 us 7.33 us: 1.04x slower
lines: 42 raw_text: "abc \t" 8.09 us 8.31 us: 1.03x slower
lines: 44 raw_text: "abc \t" 8.45 us 8.77 us: 1.04x slower
lines: 46 raw_text: "abc \t" 8.89 us 9.14 us: 1.03x slower
lines: 48 raw_text: "abc \t" 9.20 us 9.63 us: 1.05x slower
lines: 2 raw_text: " " 1.13 us 999 ns: 1.13x faster
lines: 4 raw_text: " " 1.55 us 1.44 us: 1.08x faster
lines: 6 raw_text: " " 1.86 us 1.78 us: 1.04x faster
lines: 8 raw_text: " " 2.19 us 2.08 us: 1.05x faster
lines: 10 raw_text: " " 2.49 us 2.41 us: 1.03x faster
lines: 12 raw_text: " " 2.88 us 2.86 us: 1.01x faster
lines: 18 raw_text: " " 4.27 us 4.00 us: 1.07x faster
lines: 20 raw_text: " " 4.48 us 4.33 us: 1.03x faster
lines: 22 raw_text: " " 4.75 us 4.81 us: 1.01x slower
lines: 26 raw_text: " " 5.69 us 5.76 us: 1.01x slower
lines: 30 raw_text: " " 6.37 us 6.49 us: 1.02x slower
lines: 32 raw_text: " " 6.84 us 7.08 us: 1.04x slower
lines: 34 raw_text: " " 7.27 us 7.51 us: 1.03x slower
lines: 36 raw_text: " " 7.71 us 7.86 us: 1.02x slower
lines: 2 raw_text: "\t" 1.07 us 833 ns: 1.28x faster
lines: 4 raw_text: "\t" 1.37 us 1.28 us: 1.07x faster
lines: 6 raw_text: "\t" 1.69 us 1.60 us: 1.06x faster
lines: 8 raw_text: "\t" 1.99 us 1.89 us: 1.06x faster
lines: 10 raw_text: "\t" 2.28 us 2.24 us: 1.02x faster
lines: 20 raw_text: "\t" 4.10 us 4.21 us: 1.03x slower
lines: 22 raw_text: "\t" 4.52 us 4.58 us: 1.01x slower
lines: 24 raw_text: "\t" 4.95 us 5.03 us: 1.02x slower
lines: 28 raw_text: "\t" 5.85 us 5.96 us: 1.02x slower
lines: 34 raw_text: "\t" 7.02 us 7.30 us: 1.04x slower
lines: 36 raw_text: "\t" 7.43 us 7.65 us: 1.03x slower
lines: 38 raw_text: "\t" 7.80 us 8.02 us: 1.03x slower
lines: 40 raw_text: "\t" 8.09 us 8.32 us: 1.03x slower
lines: 42 raw_text: "\t" 8.49 us 8.78 us: 1.03x slower
lines: 44 raw_text: "\t" 8.91 us 9.31 us: 1.04x slower
lines: 46 raw_text: "\t" 9.36 us 9.63 us: 1.03x slower
lines: 48 raw_text: "\t" 9.75 us 10.0 us: 1.03x slower
lines: 2 raw_text: " \t abc" 1.14 us 993 ns: 1.15x faster
lines: 4 raw_text: " \t abc" 1.57 us 1.47 us: 1.07x faster
lines: 6 raw_text: " \t abc" 1.90 us 1.79 us: 1.06x faster
lines: 8 raw_text: " \t abc" 2.22 us 2.09 us: 1.06x faster
lines: 10 raw_text: " \t abc" 2.51 us 2.43 us: 1.03x faster
lines: 12 raw_text: " \t abc" 2.96 us 2.89 us: 1.02x faster
lines: 14 raw_text: " \t abc" 3.39 us 3.27 us: 1.04x faster
lines: 20 raw_text: " \t abc" 4.44 us 4.36 us: 1.02x faster
lines: 24 raw_text: " \t abc" 5.30 us 5.38 us: 1.01x slower
lines: 30 raw_text: " \t abc" 6.47 us 6.53 us: 1.01x slower
lines: 38 raw_text: " \t abc" 8.15 us 8.33 us: 1.02x slower
lines: 40 raw_text: " \t abc" 8.46 us 8.58 us: 1.01x slower
lines: 42 raw_text: " \t abc" 8.88 us 9.10 us: 1.02x slower
lines: 44 raw_text: " \t abc" 9.26 us 9.46 us: 1.02x slower
lines: 46 raw_text: " \t abc" 9.73 us 9.91 us: 1.02x slower
lines: 48 raw_text: " \t abc" 10.2 us 10.4 us: 1.03x slower
lines: 2 raw_text: " \t abc \t " 1.15 us 997 ns: 1.16x faster
lines: 4 raw_text: " \t abc \t " 1.56 us 1.48 us: 1.06x faster
lines: 6 raw_text: " \t abc \t " 1.90 us 1.80 us: 1.06x faster
lines: 8 raw_text: " \t abc \t " 2.27 us 2.16 us: 1.05x faster
lines: 10 raw_text: " \t abc \t " 2.57 us 2.46 us: 1.05x faster
lines: 12 raw_text: " \t abc \t " 3.01 us 2.94 us: 1.02x faster
lines: 14 raw_text: " \t abc \t " 3.37 us 3.28 us: 1.03x faster
lines: 22 raw_text: " \t abc \t " 4.83 us 4.97 us: 1.03x slower
lines: 26 raw_text: " \t abc \t " 5.82 us 5.93 us: 1.02x slower
lines: 30 raw_text: " \t abc \t " 6.50 us 6.67 us: 1.03x slower
lines: 36 raw_text: " \t abc \t " 7.89 us 8.04 us: 1.02x slower
Geometric mean (ref) 1.01x faster

after analyzing all the function documentation in the Python codebase the average documentation length seems to be the following https://gist.github.com/Marius-Juston/08c574901317ee40837ab87ce0855685:

Stat Doc Length
Min 2
Max 99
Mean 10.361825192802057
Median 7

( excluding all 1 line comments )

so technically, this implementation is slightly faster for the average documentation length ( now is this performance increase really worth it, no probably not lol )

Lib/textwrap.py Outdated
l1 = min(non_blank_lines, default='')
l2 = max(non_blank_lines, default='')
margin = 0
l1 = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
l1 = None
l1 = ''

You can then skip the is None check in the loop. Similar for l2 (but you have to pick the default with care)

Lib/textwrap.py Outdated
if line and not line.isspace():
if l1 is None or line < l1:
l1 = line
if l2 is None or line > l2:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you skip the if None check, this if can be an elseif

@picnixz
Copy link
Member

picnixz commented Mar 31, 2025

I'm against this change not only because it's harder to read but also because it only offers an overall <10% speed-up. In general, we accept optimizations exceeding that threshold.

@Marius-Juston
Copy link
Contributor Author

@eendebakpt with the new changes:

Benchmark raw new new2
raw_text: "abc \t" 2.91 ms 3.33 ms: 1.15x slower 3.27 ms: 1.12x slower
raw_text: " " 4.31 ms 4.17 ms: 1.03x faster 4.16 ms: 1.03x faster
raw_text: "\t" 4.20 ms not significant 4.13 ms: 1.02x faster
raw_text: " \t abc" 4.04 ms 4.40 ms: 1.09x slower 4.37 ms: 1.08x slower
raw_text: " \t abc \t " 4.08 ms 4.40 ms: 1.08x slower 4.37 ms: 1.07x slower
raw_text: 1000 spaces 34.9 ms 27.9 ms: 1.25x faster 27.3 ms: 1.28x faster
Basic indented text with empty lines 1.88 us 1.82 us: 1.03x faster 1.83 us: 1.03x faster
Text with mixed indentation and blank lines 1.85 us 1.79 us: 1.03x faster 1.81 us: 1.03x faster
No indentation (edge case) 1.24 us 1.14 us: 1.09x faster 1.12 us: 1.11x faster
Only blank lines 958 ns 641 ns: 1.49x faster 520 ns: 1.84x faster
Edge case: No common prefix to remove 1.09 us 993 ns: 1.10x faster 962 ns: 1.14x faster
Edge case: Single indented line 998 ns 855 ns: 1.17x faster 862 ns: 1.16x faster
Edge case: Single indented line only 631 ns 432 ns: 1.46x faster 334 ns: 1.89x faster
Edge case: Empty text 89.3 ns 81.0 ns: 1.10x faster 78.9 ns: 1.13x faster
no_indent 2.57 us 2.50 us: 1.03x faster 2.44 us: 1.05x faster
mixed 10.4 us 10.9 us: 1.05x slower 10.7 us: 1.03x slower
large_text 77.7 us 85.7 us: 1.10x slower 83.5 us: 1.08x slower
whitespace_only 859 ns 599 ns: 1.43x faster 479 ns: 1.79x faster
Geometric mean (ref) 1.08x faster 1.13x faster

Small values

Benchmark raw_scale_2 new_scale_2 new2_scale_2
lines: 2 raw_text: no prefix 890 ns 764 ns: 1.16x faster 766 ns: 1.16x faster
lines: 4 raw_text: no prefix 1.25 us 1.16 us: 1.08x faster 1.17 us: 1.08x faster
lines: 6 raw_text: no prefix 1.56 us 1.47 us: 1.06x faster 1.49 us: 1.04x faster
lines: 8 raw_text: no prefix 1.84 us 1.77 us: 1.04x faster 1.77 us: 1.04x faster
lines: 10 raw_text: no prefix 2.14 us 2.08 us: 1.03x faster 2.05 us: 1.04x faster
lines: 14 raw_text: no prefix 2.82 us not significant 2.73 us: 1.03x faster
lines: 16 raw_text: no prefix 3.17 us not significant 3.06 us: 1.04x faster
lines: 18 raw_text: no prefix 3.52 us not significant 3.45 us: 1.02x faster
lines: 20 raw_text: no prefix 3.80 us 3.86 us: 1.02x slower 3.74 us: 1.02x faster
lines: 22 raw_text: no prefix 4.21 us 4.30 us: 1.02x slower 4.30 us: 1.02x slower
lines: 24 raw_text: no prefix 4.64 us 4.77 us: 1.03x slower not significant
lines: 26 raw_text: no prefix 5.10 us 5.19 us: 1.02x slower not significant
lines: 28 raw_text: no prefix 5.43 us 5.52 us: 1.02x slower not significant
lines: 30 raw_text: no prefix 5.73 us 5.81 us: 1.01x slower 5.85 us: 1.02x slower
lines: 32 raw_text: no prefix 6.20 us 6.30 us: 1.02x slower not significant
lines: 36 raw_text: no prefix 6.95 us 7.14 us: 1.03x slower not significant
lines: 38 raw_text: no prefix 7.27 us 7.49 us: 1.03x slower not significant
lines: 40 raw_text: no prefix 7.55 us 7.73 us: 1.02x slower not significant
lines: 42 raw_text: no prefix 7.89 us 8.13 us: 1.03x slower 8.06 us: 1.02x slower
lines: 44 raw_text: no prefix 8.31 us 8.54 us: 1.03x slower 8.49 us: 1.02x slower
lines: 46 raw_text: no prefix 8.78 us 8.92 us: 1.02x slower not significant
lines: 48 raw_text: no prefix 9.17 us 9.41 us: 1.03x slower not significant
lines: 2 raw_text: "abc \t" 894 ns 765 ns: 1.17x faster 777 ns: 1.15x faster
lines: 4 raw_text: "abc \t" 1.29 us 1.21 us: 1.07x faster 1.20 us: 1.07x faster
lines: 6 raw_text: "abc \t" 1.60 us 1.52 us: 1.05x faster 1.54 us: 1.04x faster
lines: 8 raw_text: "abc \t" 1.88 us 1.80 us: 1.04x faster 1.79 us: 1.05x faster
lines: 10 raw_text: "abc \t" 2.18 us 2.12 us: 1.03x faster 2.09 us: 1.04x faster
lines: 12 raw_text: "abc \t" 2.59 us 2.54 us: 1.02x faster 2.50 us: 1.03x faster
lines: 14 raw_text: "abc \t" 2.97 us 2.87 us: 1.03x faster 2.84 us: 1.05x faster
lines: 16 raw_text: "abc \t" 3.19 us 3.16 us: 1.01x faster not significant
lines: 22 raw_text: "abc \t" 4.32 us 4.38 us: 1.01x slower 4.30 us: 1.01x faster
lines: 24 raw_text: "abc \t" 4.78 us 4.91 us: 1.03x slower not significant
lines: 26 raw_text: "abc \t" 5.20 us 5.30 us: 1.02x slower not significant
lines: 28 raw_text: "abc \t" 5.57 us 5.71 us: 1.03x slower not significant
lines: 30 raw_text: "abc \t" 5.87 us 6.01 us: 1.02x slower not significant
lines: 32 raw_text: "abc \t" 6.28 us 6.52 us: 1.04x slower not significant
lines: 34 raw_text: "abc \t" 6.68 us 6.86 us: 1.03x slower 6.88 us: 1.03x slower
lines: 36 raw_text: "abc \t" 7.07 us 7.33 us: 1.04x slower not significant
lines: 42 raw_text: "abc \t" 8.09 us 8.31 us: 1.03x slower 8.34 us: 1.03x slower
lines: 44 raw_text: "abc \t" 8.45 us 8.77 us: 1.04x slower 8.61 us: 1.02x slower
lines: 46 raw_text: "abc \t" 8.89 us 9.14 us: 1.03x slower 9.07 us: 1.02x slower
lines: 48 raw_text: "abc \t" 9.20 us 9.63 us: 1.05x slower 9.48 us: 1.03x slower
lines: 2 raw_text: " " 1.13 us 999 ns: 1.13x faster 1.02 us: 1.11x faster
lines: 4 raw_text: " " 1.55 us 1.44 us: 1.08x faster 1.49 us: 1.04x faster
lines: 6 raw_text: " " 1.86 us 1.78 us: 1.04x faster not significant
lines: 8 raw_text: " " 2.19 us 2.08 us: 1.05x faster 2.09 us: 1.05x faster
lines: 10 raw_text: " " 2.49 us 2.41 us: 1.03x faster not significant
lines: 12 raw_text: " " 2.88 us 2.86 us: 1.01x faster not significant
lines: 18 raw_text: " " 4.27 us 4.00 us: 1.07x faster 3.96 us: 1.08x faster
lines: 20 raw_text: " " 4.48 us 4.33 us: 1.03x faster 4.32 us: 1.04x faster
lines: 22 raw_text: " " 4.75 us 4.81 us: 1.01x slower 4.82 us: 1.02x slower
lines: 26 raw_text: " " 5.69 us 5.76 us: 1.01x slower not significant
lines: 30 raw_text: " " 6.37 us 6.49 us: 1.02x slower 6.53 us: 1.03x slower
lines: 32 raw_text: " " 6.84 us 7.08 us: 1.04x slower 6.98 us: 1.02x slower
lines: 34 raw_text: " " 7.27 us 7.51 us: 1.03x slower 7.38 us: 1.01x slower
lines: 36 raw_text: " " 7.71 us 7.86 us: 1.02x slower 7.81 us: 1.01x slower
lines: 2 raw_text: "\t" 1.07 us 833 ns: 1.28x faster 842 ns: 1.27x faster
lines: 4 raw_text: "\t" 1.37 us 1.28 us: 1.07x faster 1.28 us: 1.07x faster
lines: 6 raw_text: "\t" 1.69 us 1.60 us: 1.06x faster 1.59 us: 1.07x faster
lines: 8 raw_text: "\t" 1.99 us 1.89 us: 1.06x faster 1.92 us: 1.04x faster
lines: 10 raw_text: "\t" 2.28 us 2.24 us: 1.02x faster 2.24 us: 1.02x faster
lines: 14 raw_text: "\t" 3.04 us not significant 2.95 us: 1.03x faster
lines: 20 raw_text: "\t" 4.10 us 4.21 us: 1.03x slower 4.03 us: 1.02x faster
lines: 22 raw_text: "\t" 4.52 us 4.58 us: 1.01x slower not significant
lines: 24 raw_text: "\t" 4.95 us 5.03 us: 1.02x slower not significant
lines: 28 raw_text: "\t" 5.85 us 5.96 us: 1.02x slower not significant
lines: 34 raw_text: "\t" 7.02 us 7.30 us: 1.04x slower 7.16 us: 1.02x slower
lines: 36 raw_text: "\t" 7.43 us 7.65 us: 1.03x slower 7.50 us: 1.01x slower
lines: 38 raw_text: "\t" 7.80 us 8.02 us: 1.03x slower 8.01 us: 1.03x slower
lines: 40 raw_text: "\t" 8.09 us 8.32 us: 1.03x slower not significant
lines: 42 raw_text: "\t" 8.49 us 8.78 us: 1.03x slower not significant
lines: 44 raw_text: "\t" 8.91 us 9.31 us: 1.04x slower 9.16 us: 1.03x slower
lines: 46 raw_text: "\t" 9.36 us 9.63 us: 1.03x slower not significant
lines: 48 raw_text: "\t" 9.75 us 10.0 us: 1.03x slower 9.94 us: 1.02x slower
lines: 2 raw_text: " \t abc" 1.14 us 993 ns: 1.15x faster 1.00 us: 1.13x faster
lines: 4 raw_text: " \t abc" 1.57 us 1.47 us: 1.07x faster 1.47 us: 1.07x faster
lines: 6 raw_text: " \t abc" 1.90 us 1.79 us: 1.06x faster 1.80 us: 1.05x faster
lines: 8 raw_text: " \t abc" 2.22 us 2.09 us: 1.06x faster 2.10 us: 1.06x faster
lines: 10 raw_text: " \t abc" 2.51 us 2.43 us: 1.03x faster 2.46 us: 1.02x faster
lines: 12 raw_text: " \t abc" 2.96 us 2.89 us: 1.02x faster 2.89 us: 1.02x faster
lines: 14 raw_text: " \t abc" 3.39 us 3.27 us: 1.04x faster 3.23 us: 1.05x faster
lines: 18 raw_text: " \t abc" 4.07 us not significant 4.00 us: 1.02x faster
lines: 20 raw_text: " \t abc" 4.44 us 4.36 us: 1.02x faster not significant
lines: 24 raw_text: " \t abc" 5.30 us 5.38 us: 1.01x slower not significant
lines: 30 raw_text: " \t abc" 6.47 us 6.53 us: 1.01x slower not significant
lines: 38 raw_text: " \t abc" 8.15 us 8.33 us: 1.02x slower 8.37 us: 1.03x slower
lines: 40 raw_text: " \t abc" 8.46 us 8.58 us: 1.01x slower 8.72 us: 1.03x slower
lines: 42 raw_text: " \t abc" 8.88 us 9.10 us: 1.02x slower not significant
lines: 44 raw_text: " \t abc" 9.26 us 9.46 us: 1.02x slower 9.49 us: 1.02x slower
lines: 46 raw_text: " \t abc" 9.73 us 9.91 us: 1.02x slower 10.0 us: 1.03x slower
lines: 48 raw_text: " \t abc" 10.2 us 10.4 us: 1.03x slower 10.4 us: 1.03x slower
lines: 2 raw_text: " \t abc \t " 1.15 us 997 ns: 1.16x faster 1.01 us: 1.14x faster
lines: 4 raw_text: " \t abc \t " 1.56 us 1.48 us: 1.06x faster 1.49 us: 1.05x faster
lines: 6 raw_text: " \t abc \t " 1.90 us 1.80 us: 1.06x faster 1.83 us: 1.04x faster
lines: 8 raw_text: " \t abc \t " 2.27 us 2.16 us: 1.05x faster 2.17 us: 1.05x faster
lines: 10 raw_text: " \t abc \t " 2.57 us 2.46 us: 1.05x faster 2.50 us: 1.03x faster
lines: 12 raw_text: " \t abc \t " 3.01 us 2.94 us: 1.02x faster 2.95 us: 1.02x faster
lines: 14 raw_text: " \t abc \t " 3.37 us 3.28 us: 1.03x faster 3.24 us: 1.04x faster
lines: 22 raw_text: " \t abc \t " 4.83 us 4.97 us: 1.03x slower 4.90 us: 1.02x slower
lines: 26 raw_text: " \t abc \t " 5.82 us 5.93 us: 1.02x slower 5.89 us: 1.01x slower
lines: 30 raw_text: " \t abc \t " 6.50 us 6.67 us: 1.03x slower 6.62 us: 1.02x slower
lines: 34 raw_text: " \t abc \t " 7.48 us not significant 7.62 us: 1.02x slower
lines: 36 raw_text: " \t abc \t " 7.89 us 8.04 us: 1.02x slower 8.01 us: 1.02x slower
lines: 42 raw_text: " \t abc \t " 8.94 us 9.13 us: 1.02x slower 9.27 us: 1.04x slower
lines: 44 raw_text: " \t abc \t " 9.47 us 9.64 us: 1.02x slower 9.64 us: 1.02x slower
lines: 46 raw_text: " \t abc \t " 9.87 us 10.1 us: 1.02x slower 10.1 us: 1.02x slower
lines: 48 raw_text: " \t abc \t " 10.3 us 10.6 us: 1.03x slower 10.5 us: 1.03x slower
lines: 2 raw_text: 1000 spaces 59.4 us 58.5 us: 1.02x faster 60.8 us: 1.02x slower
lines: 4 raw_text: 1000 spaces 63.1 us 62.0 us: 1.02x faster 64.7 us: 1.03x slower
lines: 6 raw_text: 1000 spaces 66.2 us 63.3 us: 1.05x faster not significant
lines: 8 raw_text: 1000 spaces 66.8 us 64.5 us: 1.04x faster not significant
lines: 10 raw_text: 1000 spaces 68.9 us 65.4 us: 1.05x faster not significant
lines: 12 raw_text: 1000 spaces 73.1 us 68.7 us: 1.06x faster 71.5 us: 1.02x faster
lines: 14 raw_text: 1000 spaces 74.9 us 70.3 us: 1.06x faster 73.6 us: 1.02x faster
lines: 16 raw_text: 1000 spaces 77.4 us 71.8 us: 1.08x faster 74.2 us: 1.04x faster
lines: 18 raw_text: 1000 spaces 80.8 us 73.9 us: 1.09x faster 77.5 us: 1.04x faster
lines: 20 raw_text: 1000 spaces 83.1 us 76.1 us: 1.09x faster 78.9 us: 1.05x faster
lines: 22 raw_text: 1000 spaces 86.4 us 79.2 us: 1.09x faster 82.0 us: 1.05x faster
lines: 24 raw_text: 1000 spaces 90.6 us 82.8 us: 1.09x faster 84.0 us: 1.08x faster
lines: 26 raw_text: 1000 spaces 94.1 us 83.8 us: 1.12x faster 86.8 us: 1.08x faster
lines: 28 raw_text: 1000 spaces 98.3 us 86.5 us: 1.14x faster 89.0 us: 1.10x faster
lines: 30 raw_text: 1000 spaces 100 us 88.1 us: 1.14x faster 90.5 us: 1.11x faster
lines: 32 raw_text: 1000 spaces 104 us 90.3 us: 1.15x faster 93.0 us: 1.11x faster
lines: 34 raw_text: 1000 spaces 108 us 93.3 us: 1.16x faster 95.9 us: 1.13x faster
lines: 36 raw_text: 1000 spaces 111 us 96.1 us: 1.15x faster 98.5 us: 1.12x faster
lines: 38 raw_text: 1000 spaces 112 us 98.4 us: 1.14x faster 99.6 us: 1.13x faster
lines: 40 raw_text: 1000 spaces 117 us 99.3 us: 1.17x faster 101 us: 1.15x faster
lines: 42 raw_text: 1000 spaces 124 us 102 us: 1.21x faster 105 us: 1.18x faster
lines: 44 raw_text: 1000 spaces 129 us 106 us: 1.21x faster 107 us: 1.21x faster
lines: 46 raw_text: 1000 spaces 134 us 108 us: 1.25x faster 110 us: 1.22x faster
lines: 48 raw_text: 1000 spaces 134 us 111 us: 1.21x faster 113 us: 1.19x faster
Geometric mean (ref) 1.02x faster 1.02x faster

Large file

Benchmark raw_scale new_scale new2_scale
lines: 500 raw_text: no prefix 93.0 us 99.2 us: 1.07x slower not significant
lines: 1000 raw_text: no prefix 193 us 203 us: 1.05x slower 188 us: 1.03x faster
lines: 1500 raw_text: no prefix 289 us 306 us: 1.06x slower not significant
lines: 2000 raw_text: no prefix 387 us 415 us: 1.07x slower not significant
lines: 2500 raw_text: no prefix 493 us 521 us: 1.06x slower not significant
lines: 3000 raw_text: no prefix 593 us 639 us: 1.08x slower not significant
lines: 3500 raw_text: no prefix 697 us 738 us: 1.06x slower not significant
lines: 4000 raw_text: no prefix 799 us 841 us: 1.05x slower not significant
lines: 4500 raw_text: no prefix 895 us 952 us: 1.06x slower not significant
lines: 5000 raw_text: no prefix 1.01 ms 1.07 ms: 1.06x slower not significant
lines: 5500 raw_text: no prefix 1.10 ms 1.18 ms: 1.07x slower not significant
lines: 6000 raw_text: no prefix 1.21 ms 1.29 ms: 1.07x slower not significant
lines: 6500 raw_text: no prefix 1.31 ms 1.39 ms: 1.06x slower not significant
lines: 7000 raw_text: no prefix 1.43 ms 1.52 ms: 1.07x slower not significant
lines: 7500 raw_text: no prefix 1.52 ms 1.61 ms: 1.06x slower not significant
lines: 8000 raw_text: no prefix 1.63 ms 1.73 ms: 1.06x slower not significant
lines: 8500 raw_text: no prefix 1.71 ms 1.81 ms: 1.06x slower not significant
lines: 9000 raw_text: no prefix 1.82 ms 1.92 ms: 1.05x slower not significant
lines: 9500 raw_text: no prefix 1.92 ms 2.04 ms: 1.06x slower not significant
lines: 500 raw_text: "abc \t" 89.7 us 99.2 us: 1.11x slower not significant
lines: 1000 raw_text: "abc \t" 175 us 201 us: 1.15x slower not significant
lines: 1500 raw_text: "abc \t" 265 us 300 us: 1.13x slower not significant
lines: 2000 raw_text: "abc \t" 354 us 404 us: 1.14x slower not significant
lines: 2500 raw_text: "abc \t" 446 us 523 us: 1.17x slower not significant
lines: 3000 raw_text: "abc \t" 537 us 609 us: 1.14x slower not significant
lines: 3500 raw_text: "abc \t" 628 us 708 us: 1.13x slower not significant
lines: 4000 raw_text: "abc \t" 718 us 813 us: 1.13x slower not significant
lines: 4500 raw_text: "abc \t" 803 us 920 us: 1.15x slower not significant
lines: 5000 raw_text: "abc \t" 908 us 1.02 ms: 1.13x slower not significant
lines: 5500 raw_text: "abc \t" 988 us 1.13 ms: 1.14x slower 1.02 ms: 1.03x slower
lines: 6000 raw_text: "abc \t" 1.09 ms 1.26 ms: 1.16x slower not significant
lines: 6500 raw_text: "abc \t" 1.18 ms 1.33 ms: 1.13x slower not significant
lines: 7000 raw_text: "abc \t" 1.28 ms 1.44 ms: 1.12x slower not significant
lines: 7500 raw_text: "abc \t" 1.37 ms 1.54 ms: 1.13x slower not significant
lines: 8000 raw_text: "abc \t" 1.47 ms 1.63 ms: 1.11x slower not significant
lines: 8500 raw_text: "abc \t" 1.54 ms 1.75 ms: 1.14x slower not significant
lines: 9000 raw_text: "abc \t" 1.64 ms 1.88 ms: 1.15x slower not significant
lines: 9500 raw_text: "abc \t" 1.72 ms 1.93 ms: 1.12x slower not significant
lines: 500 raw_text: " " 104 us 108 us: 1.04x slower not significant
lines: 1000 raw_text: " " 213 us 221 us: 1.04x slower not significant
lines: 1500 raw_text: " " 320 us 336 us: 1.05x slower not significant
lines: 2000 raw_text: " " 435 us 473 us: 1.09x slower not significant
lines: 2500 raw_text: " " 544 us 563 us: 1.04x slower not significant
lines: 3000 raw_text: " " 658 us 684 us: 1.04x slower not significant
lines: 3500 raw_text: " " 767 us 798 us: 1.04x slower not significant
lines: 4000 raw_text: " " 884 us 916 us: 1.04x slower not significant
lines: 4500 raw_text: " " 995 us 1.05 ms: 1.05x slower 1.02 ms: 1.03x slower
lines: 5000 raw_text: " " 1.11 ms 1.22 ms: 1.10x slower not significant
lines: 5500 raw_text: " " 1.22 ms 1.28 ms: 1.05x slower 1.21 ms: 1.01x faster
lines: 6000 raw_text: " " 1.34 ms 1.40 ms: 1.04x slower not significant
lines: 6500 raw_text: " " 1.46 ms 1.51 ms: 1.04x slower not significant
lines: 7000 raw_text: " " 1.57 ms 1.63 ms: 1.04x slower 1.60 ms: 1.02x slower
lines: 7500 raw_text: " " 1.68 ms 1.74 ms: 1.04x slower not significant
lines: 8000 raw_text: " " 1.79 ms 1.86 ms: 1.04x slower not significant
lines: 8500 raw_text: " " 1.93 ms 1.98 ms: 1.03x slower not significant
lines: 9000 raw_text: " " 2.02 ms 2.08 ms: 1.03x slower not significant
lines: 9500 raw_text: " " 2.12 ms 2.21 ms: 1.04x slower not significant
lines: 500 raw_text: "\t" 101 us 106 us: 1.05x slower not significant
lines: 1000 raw_text: "\t" 206 us 217 us: 1.05x slower not significant
lines: 1500 raw_text: "\t" 318 us 331 us: 1.04x slower not significant
lines: 2000 raw_text: "\t" 427 us 443 us: 1.04x slower 421 us: 1.01x faster
lines: 2500 raw_text: "\t" 537 us 569 us: 1.06x slower not significant
lines: 3000 raw_text: "\t" 644 us 677 us: 1.05x slower not significant
lines: 3500 raw_text: "\t" 758 us 790 us: 1.04x slower not significant
lines: 4000 raw_text: "\t" 866 us 908 us: 1.05x slower not significant
lines: 4500 raw_text: "\t" 981 us 1.03 ms: 1.05x slower not significant
lines: 5000 raw_text: "\t" 1.10 ms 1.15 ms: 1.05x slower not significant
lines: 5500 raw_text: "\t" 1.20 ms 1.26 ms: 1.05x slower not significant
lines: 6000 raw_text: "\t" 1.32 ms 1.38 ms: 1.04x slower not significant
lines: 6500 raw_text: "\t" 1.43 ms 1.50 ms: 1.06x slower not significant
lines: 7000 raw_text: "\t" 1.55 ms 1.62 ms: 1.05x slower not significant
lines: 7500 raw_text: "\t" 1.66 ms 1.72 ms: 1.04x slower not significant
lines: 8000 raw_text: "\t" 1.79 ms 1.84 ms: 1.03x slower not significant
lines: 8500 raw_text: "\t" 1.91 ms 1.97 ms: 1.03x slower not significant
lines: 9000 raw_text: "\t" 1.98 ms 2.07 ms: 1.04x slower not significant
lines: 9500 raw_text: "\t" 2.10 ms 2.19 ms: 1.04x slower not significant
lines: 500 raw_text: " \t abc" 101 us 106 us: 1.05x slower 99.5 us: 1.01x faster
lines: 1000 raw_text: " \t abc" 198 us 214 us: 1.08x slower not significant
lines: 1500 raw_text: " \t abc" 299 us 323 us: 1.08x slower not significant
lines: 2000 raw_text: " \t abc" 404 us 436 us: 1.08x slower not significant
lines: 2500 raw_text: " \t abc" 500 us 553 us: 1.11x slower not significant
lines: 3000 raw_text: " \t abc" 611 us 658 us: 1.08x slower not significant
lines: 3500 raw_text: " \t abc" 703 us 778 us: 1.11x slower not significant
lines: 4000 raw_text: " \t abc" 806 us 880 us: 1.09x slower not significant
lines: 4500 raw_text: " \t abc" 916 us 1.01 ms: 1.10x slower not significant
lines: 5000 raw_text: " \t abc" 1.01 ms 1.12 ms: 1.10x slower not significant
lines: 5500 raw_text: " \t abc" 1.12 ms 1.23 ms: 1.10x slower not significant
lines: 6000 raw_text: " \t abc" 1.22 ms 1.35 ms: 1.10x slower not significant
lines: 6500 raw_text: " \t abc" 1.33 ms 1.47 ms: 1.11x slower not significant
lines: 7000 raw_text: " \t abc" 1.43 ms 1.56 ms: 1.09x slower not significant
lines: 7500 raw_text: " \t abc" 1.52 ms 1.67 ms: 1.10x slower 1.54 ms: 1.02x slower
lines: 8000 raw_text: " \t abc" 1.64 ms 1.77 ms: 1.08x slower not significant
lines: 8500 raw_text: " \t abc" 1.72 ms 1.88 ms: 1.10x slower 1.74 ms: 1.01x slower
lines: 9000 raw_text: " \t abc" 1.83 ms 2.00 ms: 1.09x slower 1.82 ms: 1.01x faster
lines: 9500 raw_text: " \t abc" 2.28 ms 2.49 ms: 1.09x slower not significant
lines: 500 raw_text: " \t abc \t " 102 us 115 us: 1.13x slower not significant
lines: 1000 raw_text: " \t abc \t " 199 us 228 us: 1.15x slower 203 us: 1.02x slower
lines: 1500 raw_text: " \t abc \t " 303 us 326 us: 1.07x slower not significant
lines: 2000 raw_text: " \t abc \t " 400 us 438 us: 1.09x slower not significant
lines: 2500 raw_text: " \t abc \t " 504 us 549 us: 1.09x slower not significant
lines: 3000 raw_text: " \t abc \t " 611 us 659 us: 1.08x slower not significant
lines: 3500 raw_text: " \t abc \t " 715 us 768 us: 1.07x slower not significant
lines: 4000 raw_text: " \t abc \t " 816 us 884 us: 1.08x slower not significant
lines: 4500 raw_text: " \t abc \t " 912 us 1.01 ms: 1.10x slower not significant
lines: 5000 raw_text: " \t abc \t " 1.03 ms 1.12 ms: 1.09x slower not significant
lines: 5500 raw_text: " \t abc \t " 1.13 ms 1.24 ms: 1.10x slower not significant
lines: 6000 raw_text: " \t abc \t " 1.23 ms 1.35 ms: 1.09x slower not significant
lines: 6500 raw_text: " \t abc \t " 1.33 ms 1.45 ms: 1.09x slower not significant
lines: 7000 raw_text: " \t abc \t " 1.44 ms 1.56 ms: 1.08x slower not significant
lines: 7500 raw_text: " \t abc \t " 1.54 ms 1.67 ms: 1.08x slower not significant
lines: 8000 raw_text: " \t abc \t " 1.64 ms 1.78 ms: 1.09x slower not significant
lines: 8500 raw_text: " \t abc \t " 1.75 ms 1.91 ms: 1.09x slower not significant
lines: 9000 raw_text: " \t abc \t " 2.19 ms 2.30 ms: 1.05x slower not significant
lines: 9500 raw_text: " \t abc \t " 2.33 ms 2.53 ms: 1.08x slower not significant
lines: 500 raw_text: 1000 spaces 906 us 680 us: 1.33x faster 928 us: 1.02x slower
lines: 1000 raw_text: 1000 spaces 1.79 ms 1.31 ms: 1.36x faster not significant
lines: 1500 raw_text: 1000 spaces 2.67 ms 1.95 ms: 1.37x faster not significant
lines: 2000 raw_text: 1000 spaces 3.55 ms 2.62 ms: 1.36x faster not significant
lines: 2500 raw_text: 1000 spaces 4.42 ms 3.25 ms: 1.36x faster not significant
lines: 3000 raw_text: 1000 spaces 5.63 ms 3.88 ms: 1.45x faster 5.72 ms: 1.02x slower
lines: 3500 raw_text: 1000 spaces 6.67 ms 4.51 ms: 1.48x faster 6.87 ms: 1.03x slower
lines: 4000 raw_text: 1000 spaces 7.73 ms 5.60 ms: 1.38x faster 7.85 ms: 1.02x slower
lines: 4500 raw_text: 1000 spaces 8.83 ms 6.46 ms: 1.37x faster not significant
lines: 5000 raw_text: 1000 spaces 9.91 ms 7.28 ms: 1.36x faster not significant
lines: 5500 raw_text: 1000 spaces 11.0 ms 8.12 ms: 1.35x faster not significant
lines: 6000 raw_text: 1000 spaces 12.0 ms 9.02 ms: 1.34x faster 12.2 ms: 1.02x slower
lines: 6500 raw_text: 1000 spaces 13.1 ms 9.76 ms: 1.34x faster 13.2 ms: 1.01x slower
lines: 7000 raw_text: 1000 spaces 14.2 ms 10.7 ms: 1.33x faster 14.3 ms: 1.01x slower
lines: 7500 raw_text: 1000 spaces 15.5 ms 11.5 ms: 1.35x faster 15.3 ms: 1.01x faster
lines: 8000 raw_text: 1000 spaces 16.5 ms 12.3 ms: 1.34x faster not significant
lines: 8500 raw_text: 1000 spaces 17.6 ms 13.4 ms: 1.31x faster not significant
lines: 9000 raw_text: 1000 spaces 18.8 ms 14.1 ms: 1.33x faster not significant
lines: 9500 raw_text: 1000 spaces 20.0 ms 15.0 ms: 1.33x faster not significant
Geometric mean (ref) 1.02x slower 1.00x slower

So on the large files we get 0 improvement, a 13% slight improvement on the synthetic test benches and on smaller values

@Marius-Juston
Copy link
Contributor Author

I'm against this change not only because it's harder to read but also because it only offers an overall <10% speed-up. In general, we accept optimizations exceeding that threshold.

Agreed that this optimization is most likely not necessary, though this is very slightly more efficient memory wise since it does not have a non_blank_lines variable so it technically only duplicates the lines variable only twice rather than 3 times like in the current implementation ( in real world this is most definitively not really a problem just a thought )

Comment on lines +435 to +453
val = False
for i, line in enumerate(lines):
# Compute min + max concurrently + normalize others
if line and not line.isspace():
if val:
if line < l1:
l1 = line
elif line > l2:
l2 = line
else:
val = True
l1 = l2 = line

else:
lines[i] = ''

if not val or not l1:
return '\n'.join(lines)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
val = False
for i, line in enumerate(lines):
# Compute min + max concurrently + normalize others
if line and not line.isspace():
if val:
if line < l1:
l1 = line
elif line > l2:
l2 = line
else:
val = True
l1 = l2 = line
else:
lines[i] = ''
if not val or not l1:
return '\n'.join(lines)
l1 = 'z'
l2 = ''
for i, line in enumerate(lines):
# Compute min + max concurrently + normalize others
if line and not line.isspace():
if line < l1:
l1 = line
if line > l2:
l2 = line
else:
lines[i] = ''
if not l1:
return '\n'.join(lines)
margin = 0
for margin, c in enumerate(l2):
if c != l1[margin] or c not in ' \t':
break

This saves chechking the val. Performance gain is only a bit though.

I agree with the other reviewers: unless we find out a way to make this much faster, the small gains are not worth the change.

@Marius-Juston Thanks for putting in the work to get all the performance numbers!

@picnixz picnixz added the pending The issue will be closed if no feedback is provided label Mar 31, 2025
Copy link
Member

@ZeroIntensity ZeroIntensity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting the benchmarks, sometimes its hard to get contributors to quote numbers, and you've gone above and beyond with benchmarking :).

But unfortunately, the numbers make me -1 on this.

  • min/max are builtins that are written in C; they're pretty fast, especially compared to multiple lines of Python code. If we're concerned about those being slow, let's try to optimize those on the C level instead.
  • There is a significant amount of added complexity here, for seemingly not much benefit.
  • Optimizing Python code by shifting things around is generally not a good idea, because when the interpreter changes, those optimizations become obsolete. We should definitely not be quoting memory usage, number of (pointer) copies, or number of lookups; those are all things that change on an interpreter-wide level.

I've learned that it can be hard to know when to stop painting. I think we should put down the brush, and enjoy the optimization that's already landed 🙂.

@Marius-Juston
Copy link
Contributor Author

Makes sense. I am just having so much fun doing this it is definitely hard to stop lol!

@Marius-Juston
Copy link
Contributor Author

Closing this down then! ( I will be putting a feature request for the min_max implementation)

@picnixz
Copy link
Member

picnixz commented Mar 31, 2025

I will be putting a feature request for the min_max implementation

I think it's already been proposed but rejected so search through issues and https://discuss.python.org/c/ideas/6 first.

@Marius-Juston
Copy link
Contributor Author

I think it's already been proposed but rejected so search through issues and https://discuss.python.org/c/ideas/6 first.

I think I found it, the https://discuss.python.org/t/minmax-function-alongside-min-and-max/2834/73 issue got closed down. It seems like it got stale and just ignored? A shame.

@AA-Turner
Copy link
Member

Feel free to open a new thread referencing the one you found. Personally I'd suggest statistics.min_max. It might be useful to identify a few motivating examples in the stdlib / popular packages -- this will be one of the first questions you're asked on Discourse.

A

@AA-Turner AA-Turner removed the pending The issue will be closed if no feedback is provided label Apr 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants