Skip to content

add second lifetime to PyRef and PyRefMut#4720

Closed
Icxolu wants to merge 10 commits intoPyO3:mainfrom
Icxolu:pyref
Closed

add second lifetime to PyRef and PyRefMut#4720
Icxolu wants to merge 10 commits intoPyO3:mainfrom
Icxolu:pyref

Conversation

@Icxolu
Copy link
Member

@Icxolu Icxolu commented Nov 19, 2024

Companion to #4390

@Icxolu Icxolu force-pushed the pyref branch 3 times, most recently from 04fadee to b3ca8c0 Compare November 23, 2024 11:47
@davidhewitt
Copy link
Member

Thank you for making progress on this one. There's a measurable perf boost in the ordering operator benchmarks and I guess similarly this is a win for most #[pymethods], so it seems to me that we definitely do want to remove that ref counting op from PyRef.

Again the same thing that's paining me as in #4390 is I'm trying to think hard how we keep the API easy for users. Lifetimes scare Rust newbies enough, let alone two 😂

Are there any tricks we can play which avoid the complexity for the common case? Here's two I can think of:

  • We could add the double lifetime type asPyRefBorrowed and make it deref to PyRef, similar to how we have Borrowed and Bound set up. We could use the double lifetime type mostly internally. Though I think really this isn't any good, because the truth is that users should still be using PyRefBorrwed for best performance and we just duplicate the API. So it's just a hot mess and not a real option.

  • More promising, slightly crazy idea: can we change the meaning of the lifetime from PyRef<'py, T> to PyRef<'a, T>? I think with the changes to the borrow flag to be an atomic in 0.23 there's no technical dependency within PyRef of the Python attachment any more (was previously necessary for the Drop impl). I haven't actually investigated this but the only API standing in our way might be PyRef::py()? If so, maybe we can just deprecate that API for now and then in PyO3 0.26 we could switch the lifetime.

(Given that we're wanting to revisit pyclass borrow checking and locking anyway, maybe we could add new types PyClassGuard<'a, T> and PyClassGuardMut<'a, T> somewhat inspired by mutex guards. And we could just deprecate PyRef / PyRefMut to get the result we want immediately.)

Opinions welcome to which, if any, of these ideas are not crazy 😂

@Icxolu
Copy link
Member Author

Icxolu commented Nov 24, 2024

Thanks for your thoughts!

Again the same thing that's paining me as in #4390 is I'm trying to think hard how we keep the API easy for users. Lifetimes scare Rust newbies enough, let alone two 😂

That's certainly true.

I agree that introducing PyRefBorrowed doesn't really lead anywhere and would mainly duplicating the API without bringing a real benefit.

The second option sounds very interesting. Switching the lifetime and introducing it via PyClassGuard<'a, T> and PyClassGuardMut<'a, T> could make an easier migration than breaking PyRef/PyRefMut. This makes me wonder if something like this could also apply for FromPyObject. I think most types don't make use of the 'py lifetime, they either have no lifetime or need 'a.

@codspeed-hq
Copy link

codspeed-hq bot commented Feb 23, 2025

CodSpeed Performance Report

Merging #4720 will not alter performance

Comparing Icxolu:pyref (9abc0f0) with main (a95eecd)

🎉 Hooray! codspeed-rust just leveled up to 2.10.1!

A heads-up, this is a breaking change and it might affect your current performance baseline a bit. But here's the exciting part - it's packed with new, cool features and promises improved result stability 🥳!
Curious about what's new? Visit our releases page to delve into all the awesome details about this new version.

Summary

✅ 88 untouched benchmarks

@Icxolu Icxolu force-pushed the pyref branch 2 times, most recently from 2367016 to 1f82315 Compare March 7, 2025 22:43
@Icxolu Icxolu force-pushed the pyref branch 2 times, most recently from 6e1fa4b to a025fca Compare April 12, 2025 10:52
@davidhewitt
Copy link
Member

The second option sounds very interesting. Switching the lifetime and introducing it via PyClassGuard<'a, T> and PyClassGuardMut<'a, T> could make an easier migration than breaking PyRef/PyRefMut. This makes me wonder if something like this could also apply for FromPyObject. I think most types don't make use of the 'py lifetime, they either have no lifetime or need 'a.

I feel like I wrote a reply to this ages ago, but it doesn't look like it's here, so probably it got interrupted by family (sorry both for that and the long long lag to actually reply here).

I think the difference in FromPyObject is that Bound<'py, T> and Borrowed<'a, 'py, T> definitely do need the 'py lifetime and they are pretty core to the whole system.

... but maybe if we accept two traits, the second trait becomes FromPyObjectAttached<'a, 'py, T>? That is at least a new angle for us to experiment with!

@davidhewitt
Copy link
Member

As for this here, it seems then that the migration path we quite like here is to add new PyClassGuard types.

We can probably introduce those already and implement FromPyObjectBound for them, so it might be the case that sorting out these types and unlocking the perf win doesn't need to get blocked on us figuring out what to do with FromPyObject.

@davidhewitt
Copy link
Member

... but maybe if we accept two traits, the second trait becomes FromPyObjectAttached<'a, 'py, T>? That is at least a new angle for us to experiment with!

FromPyObjectBound<'a, 'py, T> which we already have may also not be a terrible name for such a type.

As a final thought, I still have a vague hope that in the long run we might be able to make the Rust compiler use the unicorn feature "contexts" to make Py<T> and Borrowed<'a, T> the only types we need, and we could drop Bound<'py, T>. It seems quite cute that if FromPyObject only had the 'a lifetime it would probably fit that future dream quite well.

@Icxolu
Copy link
Member Author

Icxolu commented Jul 11, 2025

I feel like I wrote a reply to this ages ago, but it doesn't look like it's here, so probably it got interrupted by family (sorry both for that and the long long lag to actually reply here).

No worries 😄 This is kind of a big change with lots of paths that we can take. So I expected it to take time to figure out the design.

As for this here, it seems then that the migration path we quite like here is to add new PyClassGuard types.

I started an experiment with those in #5233. Looks promising, I'll continue playing with it.

@davidhewitt
Copy link
Member

Shall we close this in favour of #5233 ?

@Icxolu
Copy link
Member Author

Icxolu commented Aug 1, 2025

I think so, yes.

@Icxolu Icxolu closed this Aug 1, 2025
@Icxolu Icxolu deleted the pyref branch August 7, 2025 17:04
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.

2 participants