-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
str::is_char_boundary - slight optimization #84751
Conversation
r? @yaahc (rust-highfive has picked a reviewer for you, use r? to override) |
@Soveu fyi - A-codegen is usually used for the codegen part of the compiler itself, not for changes to the standard library that affect performance. |
btw, can I add |
You should be able to - try pinging rustbot with |
I was thinking all the time that only members can add those fancy red |
Slightly less cryptic version https://godbolt.org/z/Y9o86ch6h pub fn is_char_boundary(s: &str, index: usize) -> bool {
match s.as_bytes().get(index) {
None => false,
Some(_) if index == 0 || index == s.len() => true,
Some(&b) => (b as i8) >= -0x40,
}
} but this swaps order of cmp and test, better benchmark all versions to check actual difference. |
|
Upps pub fn is_char_boundary(s: &str, index: usize) -> bool {
match s.as_bytes().get(index) {
None if index == s.len() => true,
None => false,
Some(_) if index == 0 => true,
Some(&b) => (b as i8) >= -0x40,
}
} But this looks bad. |
I will soon add some comments |
Plus, I want to test how it affects |
Both on
|
pub fn is_char_boundary(s: &str, index: usize) -> bool {
if index == 0 {
true
} else if index < s.len() {
unsafe { *s.as_bytes().get_unchecked(index) as i8 >= -0x40 }
} else if index == s.len() {
true
} else {
false
}
} |
@yaahc ? |
@bors try @rust-timer queue |
Awaiting bors try build completion. @rustbot label: +S-waiting-on-perf |
⌛ Trying commit 7bd9d9f with merge b8c3fd3abbd324a8ce3163e2d72ad2da5bb38d83... |
☀️ Try build successful - checks-actions |
Queued b8c3fd3abbd324a8ce3163e2d72ad2da5bb38d83 with parent 69b352e, future comparison URL. |
Finished benchmarking try commit (b8c3fd3abbd324a8ce3163e2d72ad2da5bb38d83): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
Perf changes seem to be in the noise. @bors r+ rollup=maybe |
📌 Commit 7bd9d9f has been approved by |
I was kinda expecting it, it's not a huge thing. What about binary size? |
…laumeGomez Rollup of 4 pull requests Successful merges: - rust-lang#84751 (str::is_char_boundary - slight optimization) - rust-lang#85185 (Generate not more docs than necessary) - rust-lang#85324 (Warn about unused `pub` fields in non-`pub` structs) - rust-lang#85329 (fix version_str comment) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
@klensy I'm surprised by those constructs:
Naively, I would expect to just return Is there any particular reason for the unusual branch? |
All that |
Mark `str::is_char_boundary` and `str::split_at*` unstably `const`. Tracking issues: rust-lang#131516, rust-lang#131518 First commit implements `const_is_char_boundary`, second commit implements `const_str_split_at` (which depends on `const_is_char_boundary`) ~~I used `const_eval_select` for `is_char_boundary` since there is a comment about optimizations that would theoretically not happen with the simple `const`-compatible version (since `slice::get` is not `const`ifiable) cc rust-lang#84751. I have not checked if this code difference is still required for the optimization, so it might not be worth the code complication, but 🤷.~~ This changes `str::split_at_checked` to use a new private helper function `split_at_unchecked` (copied from `split_at_mut_unchecked`) that does pointer stuff instead of `get_unchecked`, since that is not currently `const`ifiable due to using the `SliceIndex` trait.
Rollup merge of rust-lang#131520 - zachs18:const-str-split, r=Noratrieb Mark `str::is_char_boundary` and `str::split_at*` unstably `const`. Tracking issues: rust-lang#131516, rust-lang#131518 First commit implements `const_is_char_boundary`, second commit implements `const_str_split_at` (which depends on `const_is_char_boundary`) ~~I used `const_eval_select` for `is_char_boundary` since there is a comment about optimizations that would theoretically not happen with the simple `const`-compatible version (since `slice::get` is not `const`ifiable) cc rust-lang#84751. I have not checked if this code difference is still required for the optimization, so it might not be worth the code complication, but 🤷.~~ This changes `str::split_at_checked` to use a new private helper function `split_at_unchecked` (copied from `split_at_mut_unchecked`) that does pointer stuff instead of `get_unchecked`, since that is not currently `const`ifiable due to using the `SliceIndex` trait.
Current
str::is_char_boundary
implementation emits slightly more instructions, because it includes an additional branch forindex == s.len()
Just changing the place of
index == s.len()
merges it withindex < s.len()
froms.as_bytes().get(index)
This one has better codegen on every platform, except powerpc
x86 codegen
aarch64 codegen
riscv64gc codegen
example::is_char_boundary:
seqz a3, a2
xor a4, a1, a2
seqz a4, a4
or a4, a4, a3
addi a3, zero, 1
bnez a4, .LBB0_3
bgeu a2, a1, .LBB0_4
add a0, a0, a2
lb a0, 0(a0)
addi a1, zero, -65
slt a3, a1, a0
.LBB0_3:
mv a0, a3
ret
.LBB0_4:
mv a0, zero
ret
example::is_char_boundary2:
beqz a2, .LBB1_3
bgeu a2, a1, .LBB1_4
add a0, a0, a2
lb a0, 0(a0)
addi a1, zero, -65
slt a0, a1, a0
ret
.LBB1_3:
addi a0, zero, 1
ret
.LBB1_4:
xor a0, a1, a2
seqz a0, a0
ret
Link to godbolt
@rustbot label: A-codegen