perf(semantic): use split_at_mut instead of iterator in current_and_parent_mut#17182
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
62857fc to
32608c4
Compare
32608c4 to
18ed7de
Compare
split_at_mut instead of iterator in current_and_parent_mut
CodSpeed Performance ReportMerging #17182 will not alter performanceComparing Summary
Footnotes
|
|
😞 I was more optimistic that there'd be a more concrete perf improvement. |
There was a problem hiding this comment.
Pull request overview
This PR optimizes the current_and_parent_mut method in the unresolved references stack by replacing an iterator-based approach with split_at_mut, resulting in cleaner assembly code with fewer instructions and no panic paths.
Key Changes
- Replaced
iter_mut().nth().next()pattern withsplit_at_mut()for better code generation - Removed outdated godbolt.org link comment since the optimization approach has changed
- Maintained the same safety guarantees through existing
assert_unchecked!calls
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Weird. It's regressed in since Rust 1.79. Used not to have a panicking branch. You got a Godbolt link for new vs old? (just checking this isn't Claude inventing assembly again) It's also useful to put a Godbolt link in a comment above code like this, which we can use to verify the assembly is still what it should be. |
|
Yep i did verify these as claude can hallicinate here: |
|
I made an issue here rust-lang/rust#150235 it's a regression in 1.82 |
Merge activity
|
…d_parent_mut` (#17182) Before: ```rs impl Stack { #[inline] pub fn current_and_parent_iter(&mut self) -> (&mut Map, &mut Map) { // Assert invariants if self.depth == 0 { unsafe { unreachable_unchecked() }; } if self.stack.len() <= self.depth { unsafe { unreachable_unchecked() }; } let mut iter = self.stack.iter_mut(); let parent = iter.nth(self.depth - 1).unwrap(); let current = iter.next().unwrap(); (current, parent) } } pub fn use_iter(s: &mut Stack) -> (&mut Map, &mut Map) { s.current_and_parent_iter() } ``` ```asm use_iter: mov rax, qword ptr [rdi + 24] movabs rcx, 1152921504606846975 and rcx, rax cmp rcx, qword ptr [rdi + 16] je .LBB0_2 lea rcx, [rax + 2*rax] shl rcx, 4 mov rdx, qword ptr [rdi + 8] lea rax, [rdx + rcx] add rdx, rcx add rdx, -48 ret .LBB0_2: push rax lea rdi, [rip + .Lanon.f6daf3c64be0965473887c7779875594.1] call qword ptr [rip + core::option::unwrap_failed::hc7ed8ec7fd6c106d@GOTPCREL] .Lanon.f6daf3c64be0965473887c7779875594.0: .asciz "/app/example.rs" .Lanon.f6daf3c64be0965473887c7779875594.1: .quad .Lanon.f6daf3c64be0965473887c7779875594.0 .asciz "\017\000\000\000\000\000\000\000\025\000\000\000#\000\000" ``` After: ```rs impl Stack { #[inline] pub fn current_and_parent_split(&mut self) -> (&mut Map, &mut Map) { // Assert invariants if self.depth == 0 { unsafe { unreachable_unchecked() }; } if self.stack.len() <= self.depth { unsafe { unreachable_unchecked() }; } let (head, tail) = self.stack.split_at_mut(self.depth); let parent = &mut head[self.depth - 1] ; let current = &mut tail[0]; (current, parent) } } pub fn use_split(s: &mut Stack) -> (&mut Map, &mut Map) { s.current_and_parent_split() } ``` ```asm use_split: mov rcx, qword ptr [rdi + 8] mov rax, qword ptr [rdi + 24] lea rdx, [rax + 2*rax] shl rdx, 4 lea rax, [rcx + rdx] add rdx, rcx add rdx, -48 ret ```
18ed7de to
315c9ed
Compare

Before:
After: