-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # 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