Skip to content

core: optimize using integer down-counter#157686

Open
mazam-97 wants to merge 1 commit into
rust-lang:mainfrom
mazam-97:improve-performance-of-SIMD-substring-search
Open

core: optimize using integer down-counter#157686
mazam-97 wants to merge 1 commit into
rust-lang:mainfrom
mazam-97:improve-performance-of-SIMD-substring-search

Conversation

@mazam-97

@mazam-97 mazam-97 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Port the candidate check optimization from upstream  to track
the loop via an integer down-counter rather than pointer boundaries.
This eliminates byte-at-a-time loops and improves LLVM register
optimization and latency for short needles.

Source: BurntSushi/memchr@e77f0bf

Mentored-by: Usman Akinyemi <[email protected]>
Signed-off-by: Md Dilshad Azam <[email protected]>
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 10, 2026
@rustbot

rustbot commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @clarfonthey (or someone else) some time within the next two weeks.

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue
Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: libs
  • libs expanded to 10 candidates
  • Random selection from Mark-Simulacrum, clarfonthey, jhpratt

@clarfonthey

Copy link
Copy Markdown
Contributor

This looks reasonable to me, although:

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Jun 10, 2026
rust-bors Bot pushed a commit that referenced this pull request Jun 10, 2026
…g-search, r=<try>

core: optimize  using integer down-counter
@rust-bors

rust-bors Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: cc78822 (cc7882217fdf82f2f11be804b747f0244f27a55a, parent: d56483a91d6cf5041351a3208b8d08f98f0c8b56)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (cc78822): comparison URL.

Overall result: ❌✅ regressions and improvements - 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 @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.9% [0.9%, 0.9%] 1
Regressions ❌
(secondary)
0.2% [0.2%, 0.2%] 2
Improvements ✅
(primary)
-0.2% [-0.2%, -0.2%] 2
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.2% [-0.2%, 0.9%] 3

Max RSS (memory usage)

Results (primary 1.8%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
3.8% [2.9%, 4.8%] 2
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-2.4% [-2.4%, -2.4%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 1.8% [-2.4%, 4.8%] 3

Cycles

Results (secondary 2.3%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.3% [2.3%, 2.3%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

Results (primary -0.1%, secondary -0.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
0.2% [0.0%, 0.7%] 5
Regressions ❌
(secondary)
0.0% [0.0%, 0.1%] 2
Improvements ✅
(primary)
-0.2% [-0.5%, -0.0%] 9
Improvements ✅
(secondary)
-0.3% [-0.5%, -0.0%] 8
All ❌✅ (primary) -0.1% [-0.5%, 0.7%] 14

Bootstrap: 525.47s -> 518.973s (-1.24%)
Artifact size: 400.80 MiB -> 401.30 MiB (0.12%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Jun 10, 2026
@clarfonthey

Copy link
Copy Markdown
Contributor

Yeah, doesn't actually seem like this is an improvement, at least on the current compiler + LLVM version.

@mazam-97 mazam-97 closed this Jun 10, 2026
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 10, 2026
@mazam-97 mazam-97 reopened this Jun 10, 2026
@mazam-97

Copy link
Copy Markdown
Contributor Author

@clarfonthey I was trying to address the issue #157593 in which the implementation of SIMD search string was pulled from memchr library which itself got an improvement a few years ago BurntSushi/memchr@e77f0bf. So As mentioned in the issue, I pulled in the improvement here as well. Seems like it did not turn out to be as anticipated. Am I missing something?

cc. @BurntSushi

@BurntSushi

BurntSushi commented Jun 10, 2026

Copy link
Copy Markdown
Member

Not sure. I do benchmarks based on wall clock time, not instruction count. Instruction count is a good first approximation, but it's not obvious to me that it should win the day here. I'm not familiar with std's benchmarking metholody, but is there a way to run benchmarks based on the actual thing we care about? That is, wall clock timing. Not instruction count.

(The memchr improvements were measured by wall clock time.)

@clarfonthey

Copy link
Copy Markdown
Contributor

The main issue is that wall clock time is unfortunately very finicky with CI, so, using cachegrind is much more reliable. By the estimated cycle count, which is effectively an estimated wall clock time, it's just a unilateral performance degradation too, so, I'm not 100% sure this is currently working on the latest LLVM version.

I would say this kind of thing would probably be best combined with a codegen test to verify that vectorisation is actually happening as we expect, since I'm not 100% sure this version of the code is doing that on the desired targets.

@mazam-97

mazam-97 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Also it is quite surprising that the wall clock time is also showing performace degradation https://perf.rust-lang.org/compare.html?start=d56483a91d6cf5041351a3208b8d08f98f0c8b56&end=cc7882217fdf82f2f11be804b747f0244f27a55a&stat=wall-time

What I do not understand is the perf degradation rather than improvement. The change in logic clearly suggests improvement in performance.

@BurntSushi

Copy link
Copy Markdown
Member

Yeah for sure, instruction count in CI makes a lot of sense. I definitely get that aspect.

I re-ran the memchr benchmarks to gauge things here. baseline refers to memchr without the improved is_equal_raw routine. (I manually patched it into memchr 2.8.1.) And change refers to the improved one, or the status quo. Here are my benchmark results:

[andrew@duff memchr]$ rebar diff tmp/baseline.csv tmp/change.csv
benchmark                                         engine                       tmp/baseline.csv     tmp/change.csv
---------                                         ------                       ----------------     --------------
memmem/subtitles/common/huge-en-that              rust/memchr/memmem/prebuilt  32.3 GB/s (1.12x)    36.0 GB/s (1.00x)
memmem/subtitles/common/huge-en-you               rust/memchr/memmem/prebuilt  15.9 GB/s (1.00x)    15.6 GB/s (1.02x)
memmem/subtitles/common/huge-en-one-space         rust/memchr/memmem/prebuilt  1364.3 MB/s (1.01x)  1377.1 MB/s (1.00x)
memmem/subtitles/common/huge-ru-that              rust/memchr/memmem/prebuilt  30.9 GB/s (1.17x)    36.1 GB/s (1.00x)
memmem/subtitles/common/huge-ru-not               rust/memchr/memmem/prebuilt  16.4 GB/s (1.01x)    16.6 GB/s (1.00x)
memmem/subtitles/common/huge-ru-one-space         rust/memchr/memmem/prebuilt  2.6 GB/s (1.00x)     2.6 GB/s (1.00x)
memmem/subtitles/common/huge-zh-that              rust/memchr/memmem/prebuilt  30.9 GB/s (1.00x)    26.6 GB/s (1.16x)
memmem/subtitles/common/huge-zh-do-not            rust/memchr/memmem/prebuilt  19.8 GB/s (1.00x)    19.9 GB/s (1.00x)
memmem/subtitles/common/huge-zh-one-space         rust/memchr/memmem/prebuilt  5.3 GB/s (1.01x)     5.4 GB/s (1.00x)
memmem/subtitles/never/huge-en-john-watson        rust/memchr/memmem/prebuilt  42.6 GB/s (1.15x)    48.9 GB/s (1.00x)
memmem/subtitles/never/huge-en-all-common-bytes   rust/memchr/memmem/prebuilt  41.2 GB/s (1.15x)    47.4 GB/s (1.00x)
memmem/subtitles/never/huge-en-some-rare-bytes    rust/memchr/memmem/prebuilt  41.8 GB/s (1.17x)    49.0 GB/s (1.00x)
memmem/subtitles/never/huge-en-two-space          rust/memchr/memmem/prebuilt  43.0 GB/s (1.00x)    43.0 GB/s (1.00x)
memmem/subtitles/never/teeny-en-john-watson       rust/memchr/memmem/prebuilt  1668.9 MB/s (1.00x)  1668.9 MB/s (1.00x)
memmem/subtitles/never/teeny-en-all-common-bytes  rust/memchr/memmem/prebuilt  1668.9 MB/s (1.00x)  1668.9 MB/s (1.00x)
memmem/subtitles/never/teeny-en-some-rare-bytes   rust/memchr/memmem/prebuilt  1668.9 MB/s (1.00x)  1570.8 MB/s (1.06x)
memmem/subtitles/never/teeny-en-two-space         rust/memchr/memmem/prebuilt  1668.9 MB/s (1.00x)  1668.9 MB/s (1.00x)
memmem/subtitles/never/huge-ru-john-watson        rust/memchr/memmem/prebuilt  41.6 GB/s (1.13x)    47.0 GB/s (1.00x)
memmem/subtitles/never/teeny-ru-john-watson       rust/memchr/memmem/prebuilt  2.6 GB/s (1.00x)     2.6 GB/s (1.00x)
memmem/subtitles/never/huge-zh-john-watson        rust/memchr/memmem/prebuilt  42.1 GB/s (1.12x)    47.3 GB/s (1.00x)
memmem/subtitles/never/teeny-zh-john-watson       rust/memchr/memmem/prebuilt  1847.7 MB/s (1.00x)  1739.1 MB/s (1.06x)
memmem/subtitles/rare/huge-en-sherlock-holmes     rust/memchr/memmem/prebuilt  40.8 GB/s (1.13x)    46.1 GB/s (1.00x)
memmem/subtitles/rare/huge-en-sherlock            rust/memchr/memmem/prebuilt  42.4 GB/s (1.15x)    48.8 GB/s (1.00x)
memmem/subtitles/rare/huge-en-medium-needle       rust/memchr/memmem/prebuilt  40.7 GB/s (1.19x)    48.2 GB/s (1.00x)
memmem/subtitles/rare/huge-en-long-needle         rust/memchr/memmem/prebuilt  44.8 GB/s (1.00x)    44.8 GB/s (1.00x)
memmem/subtitles/rare/huge-en-huge-needle         rust/memchr/memmem/prebuilt  39.5 GB/s (1.17x)    46.3 GB/s (1.00x)
memmem/subtitles/rare/teeny-en-sherlock-holmes    rust/memchr/memmem/prebuilt  1405.4 MB/s (1.00x)  1405.4 MB/s (1.00x)
memmem/subtitles/rare/teeny-en-sherlock           rust/memchr/memmem/prebuilt  1271.6 MB/s (1.00x)  1271.6 MB/s (1.00x)
memmem/subtitles/rare/huge-ru-sherlock-holmes     rust/memchr/memmem/prebuilt  41.5 GB/s (1.13x)    46.8 GB/s (1.00x)
memmem/subtitles/rare/huge-ru-sherlock            rust/memchr/memmem/prebuilt  41.6 GB/s (1.13x)    47.0 GB/s (1.00x)
memmem/subtitles/rare/teeny-ru-sherlock-holmes    rust/memchr/memmem/prebuilt  2002.7 MB/s (1.05x)  2.1 GB/s (1.00x)
memmem/subtitles/rare/teeny-ru-sherlock           rust/memchr/memmem/prebuilt  1741.5 MB/s (1.05x)  1820.7 MB/s (1.00x)
memmem/subtitles/rare/huge-zh-sherlock-holmes     rust/memchr/memmem/prebuilt  43.1 GB/s (1.07x)    45.9 GB/s (1.00x)
memmem/subtitles/rare/huge-zh-sherlock            rust/memchr/memmem/prebuilt  42.7 GB/s (1.17x)    49.9 GB/s (1.00x)
memmem/subtitles/rare/teeny-zh-sherlock-holmes    rust/memchr/memmem/prebuilt  1182.6 MB/s (1.00x)  1182.6 MB/s (1.00x)
memmem/subtitles/rare/teeny-zh-sherlock           rust/memchr/memmem/prebuilt  1137.1 MB/s (1.04x)  1182.6 MB/s (1.00x)

This is very different than the initial measurements a few years ago:

[andrew@duff memchr]$ rebar diff -e memchr/memmem tmp/base.csv tmp/simpler-is-equal-raw.csv -t 1.1
benchmark                                          engine                       tmp/base.csv       tmp/simpler-is-equal-raw.csv
---------                                          ------                       ------------       ----------------------------
memmem/code/rust-library-never-fn-strength         rust/memchr/memmem/prebuilt  40.6 GB/s (1.24x)  50.2 GB/s (1.00x)
memmem/code/rust-library-never-fn-strength-paren   rust/memchr/memmem/prebuilt  39.4 GB/s (1.30x)  51.3 GB/s (1.00x)
memmem/code/rust-library-never-fn-quux             rust/memchr/memmem/prebuilt  41.0 GB/s (1.25x)  51.4 GB/s (1.00x)
memmem/code/rust-library-rare-fn-from-str          rust/memchr/memmem/prebuilt  38.8 GB/s (1.35x)  52.4 GB/s (1.00x)
memmem/code/rust-library-common-fn-is-empty        rust/memchr/memmem/prebuilt  40.2 GB/s (1.25x)  50.3 GB/s (1.00x)
memmem/code/rust-library-common-fn                 rust/memchr/memmem/prebuilt  24.7 GB/s (1.18x)  29.2 GB/s (1.00x)
memmem/code/rust-library-common-let                rust/memchr/memmem/prebuilt  18.1 GB/s (1.10x)  20.0 GB/s (1.00x)
memmem/pathological/md5-huge-no-hash               rust/memchr/memmem/prebuilt  38.5 GB/s (1.30x)  50.0 GB/s (1.00x)
memmem/pathological/md5-huge-last-hash             rust/memchr/memmem/prebuilt  38.0 GB/s (1.32x)  50.1 GB/s (1.00x)
memmem/pathological/rare-repeated-huge-tricky      rust/memchr/memmem/prebuilt  36.6 GB/s (1.72x)  62.8 GB/s (1.00x)
memmem/pathological/defeat-simple-vector-alphabet  rust/memchr/memmem/prebuilt  4.0 GB/s (1.33x)   5.4 GB/s (1.00x)
memmem/sliceslice/seemingly-random                 rust/memchr/memmem/prebuilt  8.1 MB/s (1.17x)   9.5 MB/s (1.00x)
memmem/sliceslice/i386                             rust/memchr/memmem/prebuilt  42.7 MB/s (1.25x)  53.5 MB/s (1.00x)
memmem/subtitles/common/huge-en-that               rust/memchr/memmem/prebuilt  30.7 GB/s (1.22x)  37.4 GB/s (1.00x)
memmem/subtitles/common/huge-ru-that               rust/memchr/memmem/prebuilt  28.8 GB/s (1.26x)  36.2 GB/s (1.00x)
memmem/subtitles/common/huge-ru-one-space          rust/memchr/memmem/prebuilt  2.6 GB/s (1.00x)   2.3 GB/s (1.12x)
memmem/subtitles/common/huge-zh-that               rust/memchr/memmem/prebuilt  27.2 GB/s (1.40x)  38.2 GB/s (1.00x)
memmem/subtitles/common/huge-zh-do-not             rust/memchr/memmem/prebuilt  18.0 GB/s (1.14x)  20.4 GB/s (1.00x)
memmem/subtitles/never/huge-en-john-watson         rust/memchr/memmem/prebuilt  39.8 GB/s (1.55x)  61.6 GB/s (1.00x)
memmem/subtitles/never/huge-en-all-common-bytes    rust/memchr/memmem/prebuilt  41.1 GB/s (1.25x)  51.4 GB/s (1.00x)
memmem/subtitles/never/huge-en-some-rare-bytes     rust/memchr/memmem/prebuilt  40.2 GB/s (1.57x)  63.0 GB/s (1.00x)
memmem/subtitles/never/huge-en-two-space           rust/memchr/memmem/prebuilt  33.2 GB/s (1.89x)  62.8 GB/s (1.00x)
memmem/subtitles/never/huge-ru-john-watson         rust/memchr/memmem/prebuilt  39.2 GB/s (1.61x)  63.1 GB/s (1.00x)
memmem/subtitles/never/huge-zh-john-watson         rust/memchr/memmem/prebuilt  39.6 GB/s (1.43x)  56.6 GB/s (1.00x)
memmem/subtitles/rare/huge-en-sherlock-holmes      rust/memchr/memmem/prebuilt  39.3 GB/s (1.55x)  61.0 GB/s (1.00x)
memmem/subtitles/rare/huge-en-sherlock             rust/memchr/memmem/prebuilt  43.5 GB/s (1.25x)  54.2 GB/s (1.00x)
memmem/subtitles/rare/huge-en-medium-needle        rust/memchr/memmem/prebuilt  32.9 GB/s (1.60x)  52.7 GB/s (1.00x)
memmem/subtitles/rare/huge-ru-sherlock-holmes      rust/memchr/memmem/prebuilt  38.7 GB/s (1.58x)  61.0 GB/s (1.00x)
memmem/subtitles/rare/huge-ru-sherlock             rust/memchr/memmem/prebuilt  39.5 GB/s (1.51x)  59.4 GB/s (1.00x)
memmem/subtitles/rare/huge-zh-sherlock-holmes      rust/memchr/memmem/prebuilt  35.9 GB/s (1.29x)  46.3 GB/s (1.00x)
memmem/subtitles/rare/huge-zh-sherlock             rust/memchr/memmem/prebuilt  35.5 GB/s (1.56x)  55.1 GB/s (1.00x)

The gap is just a lot smaller than it was before.

Not to say that a non-improvement in memchr means a non-improvement here. This routine gets inlined, and so some part of it has to do with how the compiler organizes it with the surrounding code, which is usually very hot. Folks can read BurntSushi/memchr#139 for more info, where it basically came down to removing a single mov in the critical path.

Anyway, I'm out of ideas here. I'd still try wall clock measurement personally. But it might just be the case that llvm has gotten better over the intervening years.

@BurntSushi

Copy link
Copy Markdown
Member

What I do not understand is the perf degradation ratehr than improvement. The change in logic clearly suggests improvement in performance.

I think this is a flag for "let's look at the actual Assembly generated." The critical path should be very small in the SIMD routine, so it shouldn't be too difficult to trace through.

@clarfonthey

Copy link
Copy Markdown
Contributor

Yeah, we do already have codegen tests for this sort of thing, so, I think it would be fair to add one here too. It should track the LLVM IR generated and we can trust that vector ops will turn into the right SSE/LX/NEON instructions.

@clarfonthey

Copy link
Copy Markdown
Contributor

@rustbot author

(mostly just to clarify more needs to be done; please feel free to continue asking questions!)

To provide a bit more info, the codegen tests I was talking about are here: https://github.com/rust-lang/rust/tree/main/tests/codegen-llvm

The folder structure here is mostly arbitrary, but lib-optimizations feels like the right place to put a test for this. The syntax of these should be relatively straightforward, although since they get compiled as separate crates, you may want to export this function as #[doc(hidden)] with an unstable feature like slice_internals if you want to call it directly. Alternatively, you can rely on optimisations and just ensure that things work as expected for short slices (less than 16 bytes, if I'm reading correctly?) and verify that memcmp isn't called and that the code is using SIMD instructions.

I do feel like we should be having codegen tests for this anyway, since this is the kind of thing that can break whenever LLVM is upgraded, and it's mostly about just finding the right code that LLVM will autovectorise for us.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Jun 10, 2026
@rustbot

rustbot commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@@ -1951,59 +1927,55 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
#[inline]
unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {

@scottmcm scottmcm Jun 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

unsure: how small is small here? We have https://doc.rust-lang.org/std/intrinsics/fn.raw_eq.html that we use for known-size things, so I wonder if there's a place to try something like just listing every possible length in a match and letting the compiler chew on that.

View changes since the review

@scottmcm scottmcm Jun 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alternatively, I wonder if an assert_unchecked plus a call to https://doc.rust-lang.org/std/intrinsics/fn.compare_bytes.html here would give LLVM to freedom to handle it well.

Specifically the advantage of memcmp is that it communicates the "this is readable and not undef" which offers various kinds of freedom. (Though sadly there's no memcmp.inline option the way there is llvm.memcpy.inline.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reading the code that calls this, it appeared that the answer was 0-15 bytes, but not 100% sure on that number.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds right to me. And in practice I bet it's smaller, but that requires some kind of data analysis on usage. i.e., Smaller than a pointer is I bet very common.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Very interested to see how other approaches fair here. I have less flexibility in memchr due to not being able to use unstable APIs, but a match expression would be an interesting experiment. (In memchr, the lengths might go up to 32 or something though.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here's a phrasing that's 100% stable but still does the raw_eqs, in case you're interested in playing with it @mazam-97 https://rust.godbolt.org/z/s8vGrzxxo

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Love it

@scottmcm

Copy link
Copy Markdown
Member

As an overall note here, LLVM is currently working on being able to auto-vectorize short-circuiting loops that it didn't used to be able to do. I don't know what it needs to do that -- the historical trouble is that it doesn't know which parts are readable when the rust code short-circuits -- but as a result the other thing you could consider here is looking at the broader loop and whether there's now a way to convey the important part to LLVM so it can do this in a smarter way than we can ever realistically write in libcore.

@saethlin

Copy link
Copy Markdown
Member

The main issue is that wall clock time is unfortunately very finicky with CI, so, using cachegrind is much more reliable.

Are you saying this with regard to the perf report? Those numbers are not collected in CI, they are collected on a dedicated physical machine that has specific settings applied to stabilize its behavior.

What I do not understand is the perf degradation ratehr than improvement. The change in logic clearly suggests improvement in performance.

The significance factors listed in the rightmost column suggest that these are more likely noise than anything real.

I think the right conclusion from the rustc-perf report is "no effect", and I would defer to a dedicated benchmark suite for this code path, and a codegen test sounds like a good idea.

@clarfonthey

Copy link
Copy Markdown
Contributor

Are you saying this with regard to the perf report? Those numbers are not collected in CI, they are collected on a dedicated physical machine that has specific settings applied to stabilize its behavior.

This is not something I knew about; I was under the impression cachegrind was chosen specifically to avoid environment noise. That's good to know.

@Noratrieb

Copy link
Copy Markdown
Member

rustc-perf is not really useful for evaluating library performance changes in general.
The compiler is a very specific workload that doesn't care about the performance of most parts of the standard library, and changes to the standard library can have compile time impact that is then combined with the run-time of the compiler.
rustc-perf is first and foremost a tool for evaluating the compile time performance impact of changes to the compiler. All other uses require very careful interpretation (and basically ignoring the main message and only looking at the individual results).

@the8472

the8472 commented Jun 27, 2026

Copy link
Copy Markdown
Member

library/alloctests/benches/str.rs contains a few microbenchmarks for this that you can run with ./x bench

@clarfonthey

Copy link
Copy Markdown
Contributor

Would also be particularly interested in @scottmcm's suggestions to use raw_eq are particularly fruitful. I think that the more concisely we can tell the backend what to do, the better the performance will be, since the optimiser can probably handle it better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf-regression Performance regression. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

possibly improve performance of SIMD candidate check in substring search

9 participants