Skip to content

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

Merged
davidhewitt merged 3 commits intoPyO3:mainfrom
davidhewitt:bound-as-any
Feb 1, 2024
Merged

add Bound::as_any and Bound::into_any (and same for Py)#3785
davidhewitt merged 3 commits intoPyO3:mainfrom
davidhewitt:bound-as-any

Conversation

@davidhewitt
Copy link
Copy Markdown
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
@codspeed-hq
Copy link
Copy Markdown

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
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
Member Author

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

@Icxolu
Copy link
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
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 😄

///
/// # Safety
/// `ptr` must point to a Python object of type T.
#[inline]
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Member Author

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

src/instance.rs Outdated
// 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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Member Author

Thanks again both! 🚀

@davidhewitt davidhewitt enabled auto-merge February 1, 2024 10:15
@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
@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