-
Notifications
You must be signed in to change notification settings - Fork 1.9k
perf: optimize left function by eliminating double chars() iteration #19571
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
base: main
Are you sure you want to change the base?
Conversation
413e195 to
08f3cdb
Compare
|
Can you make a PR with just the benchmarks (which is easier to merge) so we can then compare main vs. changed? |
|
Opened #19600. |
| chars_buf.extend(string.chars()); | ||
| let len = chars_buf.len() as i64; | ||
|
|
||
| Some(if n.abs() < len { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor improvement: calling .abs() on MIN will lead to problems (crash in dev and wrapping in release).
i64::MIN is a corner case, so I guess no one will complain that it is not supported here.
I also tried with if n + len > 0 but then it was 3% slower on my machine.
| Some(if n.abs() < len { | |
| Some(if n != i64::MIN && n.abs() < len { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced n.abs() < len with n > -len to avoid panic/wrap on i64::MIN now.
For negative n values, the function was calling string.chars() twice: 1. Once to count total characters 2. Again to take the prefix This optimization collects chars into a reusable buffer once per row for the negative n case, eliminating the redundant iteration. Benchmark results (negative n, which triggers the optimization): - size=1024: 71.323 µs → 52.760 µs (26.0% faster) - size=4096: 289.62 µs → 212.23 µs (26.7% faster) Benchmark results (positive n, minimal overhead): - size=1024: 24.465 µs → 24.691 µs (0.9% slower) - size=4096: 96.129 µs → 97.078 µs (1.0% slower) The dramatic improvement for negative n cases far outweighs the negligible overhead for positive n cases.
Replaced `n.abs() < len` with `n > -len` to avoid panic/wrap on i64::MIN. The original code used .abs() on a negative i64 value, which causes: - Panic in debug builds when n = i64::MIN (overflow check) - Wrapping behavior in release builds (undefined behavior) The new condition `n > -len` is mathematically equivalent: - When n < 0: n.abs() = -n - So: -n < len is equivalent to n > -len This handles the i64::MIN edge case safely without performance impact, as it's just a comparison with negated value instead of abs(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
For negative n values, the function was calling string.chars() twice:
This optimization collects chars into a reusable buffer once per row for the negative n case, eliminating the redundant iteration.
Benchmark results (negative n, which triggers the optimization):
Benchmark results (positive n, minimal overhead):
The dramatic improvement for negative n cases far outweighs the negligible overhead for positive n cases.
Which issue does this PR close?
Rationale for this change
What changes are included in this PR?
Are these changes tested?
Are there any user-facing changes?