Skip to content

Optimize Iterator for IIterator<T> - #3476

Merged
Kenny Kerr (kennykerr) merged 2 commits into
masterfrom
HasCurrent
Feb 7, 2025
Merged

Kenny Kerr (kennykerr) merged 2 commits into
masterfrom
HasCurrent

Conversation

@kennykerr

@kennykerr Kenny Kerr (kennykerr) commented Feb 6, 2025 •

Copy link
Copy Markdown
Collaborator

C++/WinRT uses exceptions for error propagation and unfortunately exceptions continue to be very slow. I hadn't realized how much slower until a customer shared some logs. I decided to take a fresh look and compare the Rust implementation of IIterator<T>::Current returning E_BOUNDS to C++/WinRT's implementation that throws before returning E_BOUNDS. For a million calls:

  • Rust: 0.01s
  • C++/WinRT: 18.93s
  • C++/WinRT (without call to RoOriginate): 3.95s

So that's quite a difference. Ideally we can get C++/WinRT to avoid exceptions but this probably merits a tweak in Rust to avoid this pathological case as changing C++ is hard.

This change tweaks the Iterator trait implementation on the Rust side to use HasCurrent to avoid calls to Current and thus avoid those exceptions. With this change, the cost of iteration for C++ implementations approaches that of Rust.

Thanks to James McNellis (@JamesMcNellis) for prodding me to look into this a bit more closely. He also gave a great talk on how exceptions work that you should definitely check out.

Comment on lines +464 to +468
let result = if self.HasCurrent().unwrap_or(false) {
self.Current().ok()
} else {
None
};

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is the only change - the rest are the output of code generation used for validation.

@Ant4g0n1st

Copy link
Copy Markdown

Nice!

@kennykerr
Kenny Kerr (kennykerr) merged commit 35c31ad into master Feb 7, 2025
@kennykerr
Kenny Kerr (kennykerr) deleted the HasCurrent branch February 7, 2025 02:56
@JamesMcNellis

Copy link
Copy Markdown
Collaborator

lgtm

@ChrisGuzak

Chris Guzak (ChrisGuzak) commented Sep 17, 2025 •

Copy link
Copy Markdown
Member

2 calls is not a problem for in process APIs. but for out of process APIs, the cost per call is very high.
Kenny Kerr (@kennykerr) FYI

@kennykerr

Copy link
Copy Markdown
Collaborator Author

Yes, in pathological cases you'd want to use GetMany.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants