Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Bound::as_any and Bound::into_any (and same for Py) #3785

Merged
merged 3 commits into from
Feb 1, 2024

Conversation

davidhewitt
Copy link
Member

Split from #3606

This PR adds new methods Bound::as_any() and Bound::into_any() which can be used to cast Bound<T> into Bound<PyAny>.

I found these useful when testing out the new Bound API downstream in pydantic-core.

@davidhewitt davidhewitt added the CI-skip-changelog Skip checking changelog entry label Jan 30, 2024
Copy link

codspeed-hq bot commented Jan 30, 2024

CodSpeed Performance Report

Merging #3785 will degrade performances by 11.44%

Comparing davidhewitt:bound-as-any (49a57df) with main (516c085)

Summary

⚡ 2 improvements
❌ 1 regressions
✅ 76 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Benchmarks breakdown

Benchmark main davidhewitt:bound-as-any Change
list_via_downcast 185 ns 157.2 ns +17.67%
not_a_list_via_downcast 215 ns 242.8 ns -11.44%
sequence_from_tuple 425 ns 369.4 ns +15.04%

@Icxolu
Copy link
Contributor

Icxolu commented Jan 31, 2024

I also think these are useful additions. I think I looked for these a few time while porting tests and examples to the new API.

I not really a fan of mem::transmute, because it can easily turn into a footgun during refactoring, but I'm not sure if the alternatives here are really that much better.

@davidhewitt
Copy link
Member Author

Thanks, that's good to have support that these proposals make sense 👍

I think the main thing to consider before merge would be if there's better names than as_any and into_any; personally I think that they're short and reasonably obvious what they do.

I think I can avoid the transmute for the &self case, so let's fix that, at least...

@davidhewitt
Copy link
Member Author

I managed to find alternatives to the transmute for both implementations, so that's an improvement!

@Icxolu
Copy link
Contributor

Icxolu commented Jan 31, 2024

I managed to find alternatives to the transmute for both implementations, so that's an improvement!

Cool, I like these implementations a lot more. They might be a little longer, but don't feel so "unsafe".

As for the naming, I like them. I think they convey the intent well, and read nicely when written in code.

Copy link
Member

@adamreichold adamreichold left a comment

Choose a reason for hiding this comment

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

If we are going out of our way to avoid transmute, I think we should audit at least the other conversions in this module to make their implementation styles consistent.

@davidhewitt davidhewitt force-pushed the bound-as-any branch 2 times, most recently from b08d2fe to 33c68be Compare February 1, 2024 09:28
@davidhewitt davidhewitt changed the title add Bound::as_any and Bound::into_any add Bound::as_any and Bound::into_any (and same for Py) Feb 1, 2024
@davidhewitt
Copy link
Member Author

That's a very good suggestion. I've pushed two more commits, which carry out the following:

  • Add Py::as_any and Py::into_any, to match the first commit.
  • Use all of these new safe methods to remove a lot of the unsafe { } parts of the existing code. Where it seemed obvious I also added #[inline] hints, as most of these conversions can be inlined away to nothing.

No more transmute in the whole module, and a lot more consistency 😄

@@ -1243,24 +1270,16 @@ impl<T> Py<T> {
///
/// # Safety
/// `ptr` must point to a Python object of type T.
#[inline]
Copy link
Member Author

Choose a reason for hiding this comment

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

Removed inline here because it's only used in this module, I figure it'll be inlined anyway.

Copy link
Member

Choose a reason for hiding this comment

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

While it is available for inlining (just because it is in the same crate), #[inline] will still increase the likelyhood that it actually is inlined, i.e. it will bias the heuristics towards using that option, i.e. I would not remove it since this should be compiled away completed only after inlining which should happen immediately in the first pass of LLVM over the resulting MIR.

@davidhewitt
Copy link
Member Author

(The only code I didn't touch is changed already in #3776.)

src/instance.rs Outdated
pub fn unbind(self) -> Py<T> {
// Safety: the type T is known to be correct and the ownership of the
// pointer is transferred to the new Py<T> instance.
unsafe { Py::from_non_null(self.into_non_null()) }
unsafe { Py::from_non_null(ManuallyDrop::new(self).1 .0) }
Copy link
Member

Choose a reason for hiding this comment

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

This looks a bit weird. Maybe an intermediate binding or some parentheses could improve it?

Copy link
Member Author

Choose a reason for hiding this comment

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

For now I'll go for

        let non_null = (ManuallyDrop::new(self).1).0;
        unsafe { Py::from_non_null(non_null) }

let ptr = self.0.as_ptr();
std::mem::forget(self);
ptr
ManuallyDrop::new(self).0.as_ptr()
Copy link
Member

Choose a reason for hiding this comment

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

This is a nice idiom of using ManuallyDrop for conciseness instead of safety, compared to using forget!

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it only works when pulling a Copy field out of the ManuallyDrop, but fortunately that's what we're doing here! 😂

@davidhewitt
Copy link
Member Author

Thanks again both! 🚀

@davidhewitt davidhewitt added this pull request to the merge queue Feb 1, 2024
Merged via the queue into PyO3:main with commit d35a6a1 Feb 1, 2024
34 of 36 checks passed
@davidhewitt davidhewitt deleted the bound-as-any branch February 1, 2024 11:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI-skip-changelog Skip checking changelog entry
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants