Skip to content

Optimize Iterator for IIterator<T>#3476

Merged
kennykerr merged 2 commits intomasterfrom
HasCurrent
Feb 7, 2025
Merged

Optimize Iterator for IIterator<T>#3476
kennykerr merged 2 commits intomasterfrom
HasCurrent

Conversation

@kennykerr
Copy link
Copy Markdown
Collaborator

@kennykerr kennykerr commented Feb 6, 2025

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 @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 kennykerr merged commit 35c31ad into master Feb 7, 2025
@kennykerr kennykerr deleted the HasCurrent branch February 7, 2025 02:56
@JamesMcNellis
Copy link
Copy Markdown
Collaborator

lgtm

@ChrisGuzak
Copy link
Copy Markdown
Member

ChrisGuzak commented Sep 17, 2025

2 calls is not a problem for in process APIs. but for out of process APIs, the cost per call is very high.
@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