Single-pass ASCII lower/upper case conversion - #160480
Conversation
|
r? @JohnTitor rustbot has assigned @JohnTitor. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
@bors r+ |
… r=joshtriplett Single-pass ASCII lower/upper case conversion Current algorithm is cloning whole string/vector once, then rereads and rewrites it in-place once again which is sub-optimal and waste of CPU cycles and cache. This one creates it in a single-pass while copying the data from the original vector. In my benchmarks this one is about 1.2x-2x to 10x(KBs long strings) faster.
…uwer Rollup of 22 pull requests Successful merges: - #160426 (`rust-analyzer` subtree update) - #160372 (Derive the allocator used by tools from rustc's allocator) - #146882 (fully deprecate the legacy integral modules) - #158727 (std: use `readdir` on nearly all UNIX platforms) - #159727 (Various steps in moving away from the big reflection enum to reflection functions) - #160443 (normalize in relations, not generalize, when relating infer with alias) - #160457 (implement -Zllvm-target-feature) - #160480 (Single-pass ASCII lower/upper case conversion) - #160502 (Reduce number of miri tests executed on PR CI) - #157430 (std::random: use little-endian for reproducibility) - #158110 (fix macro attribute feature-gate span) - #159975 (Use real ThinVec in StmtDebugInfos) - #160001 (Suggest mutable method when iterating over binding) - #160024 (Fix mono reachability with no-op landing pads) - #160154 (Add regression test for HRTB associated type projection closure) - #160176 (No more `tests/ui/issues`!) - #160326 (Remove hidden_glob_reexports) - #160407 (Add regression tests for a number of ICEs and diagnostics issues labelled `E-needs-test`) - #160430 (bootstrap: Don't produce mutated/filtered PathSets during command-line matching) - #160472 (Minor fixes to `core::io` & `alloc::io` Documentation) - #160486 (Remove unused `FreeRegionsVisitor`) - #160496 (clarify non-determinism docs for algebraic operations) Failed merges: - #160501 (Add bootstrap CLI snapshot test for testing miri)
Rollup merge of #160480 - fereidani:to_ascii_upperlowercase, r=joshtriplett Single-pass ASCII lower/upper case conversion Current algorithm is cloning whole string/vector once, then rereads and rewrites it in-place once again which is sub-optimal and waste of CPU cycles and cache. This one creates it in a single-pass while copying the data from the original vector. In my benchmarks this one is about 1.2x-2x to 10x(KBs long strings) faster.
…uwer Rollup of 22 pull requests Successful merges: - rust-lang/rust#160426 (`rust-analyzer` subtree update) - rust-lang/rust#160372 (Derive the allocator used by tools from rustc's allocator) - rust-lang/rust#146882 (fully deprecate the legacy integral modules) - rust-lang/rust#158727 (std: use `readdir` on nearly all UNIX platforms) - rust-lang/rust#159727 (Various steps in moving away from the big reflection enum to reflection functions) - rust-lang/rust#160443 (normalize in relations, not generalize, when relating infer with alias) - rust-lang/rust#160457 (implement -Zllvm-target-feature) - rust-lang/rust#160480 (Single-pass ASCII lower/upper case conversion) - rust-lang/rust#160502 (Reduce number of miri tests executed on PR CI) - rust-lang/rust#157430 (std::random: use little-endian for reproducibility) - rust-lang/rust#158110 (fix macro attribute feature-gate span) - rust-lang/rust#159975 (Use real ThinVec in StmtDebugInfos) - rust-lang/rust#160001 (Suggest mutable method when iterating over binding) - rust-lang/rust#160024 (Fix mono reachability with no-op landing pads) - rust-lang/rust#160154 (Add regression test for HRTB associated type projection closure) - rust-lang/rust#160176 (No more `tests/ui/issues`!) - rust-lang/rust#160326 (Remove hidden_glob_reexports) - rust-lang/rust#160407 (Add regression tests for a number of ICEs and diagnostics issues labelled `E-needs-test`) - rust-lang/rust#160430 (bootstrap: Don't produce mutated/filtered PathSets during command-line matching) - rust-lang/rust#160472 (Minor fixes to `core::io` & `alloc::io` Documentation) - rust-lang/rust#160486 (Remove unused `FreeRegionsVisitor`) - rust-lang/rust#160496 (clarify non-determinism docs for algebraic operations) Failed merges: - rust-lang/rust#160501 (Add bootstrap CLI snapshot test for testing miri)
|
@rust-timer build 4c170e1 |
This comment has been minimized.
This comment has been minimized.
|
^ Expected a false positive, all PRs in the rollup have this same result |
|
Finished benchmarking commit (4c170e1): comparison URL. Overall result: ❌ regressions - please read:Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf. Next, please: If you can, justify the regressions found in this try perf run in writing along with @bors rollup=never rustc-perf Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary 0.9%, secondary 20.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary 0.9%, secondary 8.9%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary 0.6%, secondary 1.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 489.825s -> 489.295s (-0.11%) |
|
Is this a false positive or caused by my PR? |
|
This result is a little bit more extreme than on other PRs but given all the noise it's hard to trust the numbers... Posted a revert to see the real result, which I expect to be neutral (in which case I'll close the revert): #160703 |
|
Based on the result from the revert, this is not a false positive, but the impact is probably not from running the code itself, but from compiling it. You can see that in detailed results, there's a lot more queries related to generics, which is probably because this PR changes few plain function calls into iterator calls. It's also visible in depgrah size and metadata size increases. |
|
Given that the compiletime is slower, but the function itself is faster (since the function was benchmarked by the author), I'd lean towards not reverting and accepting the regression. I'm a bit surprised that this single function can cause such a significant regression tho |
|
Yea, it's somewhat surprising. I may be wrong, though. On the other hand I would also lean towards not reverting, but it'd be nice to investigate a bit what's going on. I might look at it today if kids allow me to 😄 |
|
The const_eval_select!(
@capture { s: &[u8] } -> bool:
if const {
is_ascii_simple(s)
} else {
...complex strategy
}
)I think we can do the same for this function too for further improvements. Original code was using this with const-hack. pub const fn make_ascii_lowercase(&mut self) {
// FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions.
let mut i = 0;
while i < self.len() {
let byte = &mut self[i];
byte.make_ascii_lowercase();
i += 1;
}
}I wonder if we do I can send a PR, just let me know. |
|
I'd avoid Either way, it doesn't seem to me that the problem is in the function. If I just put the two versions side by side in godbolt, the new one clearly looks better. It's actually almost identical except that the new doesn't have a call to memcpy. If there really is a runtime performance problem, it would probably be some interaction with other optimizations when this is inlined to some caller. |
Current algorithm is cloning whole string/vector once, then rereads and rewrites it in-place once again which is sub-optimal and waste of CPU cycles and cache.
This one creates it in a single-pass while copying the data from the original vector.
In my benchmarks this one is about 1.2x-2x to 10x(KBs long strings) faster.