-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Reduce binary size overhead of new-style constructors #1014
Conversation
include/pybind11/pybind11.h
Outdated
@@ -472,6 +490,18 @@ class cpp_function : public function { | |||
size_t args_to_copy = std::min(pos_args, n_args_in); | |||
size_t args_copied = 0; | |||
|
|||
// 0. Inject new-style `self` argument | |||
if (it->is_new_style_constructor) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really minor: it->
-> func.
, for consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I missed that. Fixed now.
tests/test_factory_constructors.py
Outdated
@@ -352,9 +351,9 @@ def test_reallocations(capture, msg): | |||
create_and_destroy(1.5) | |||
assert msg(capture) == strip_comments(""" | |||
noisy new # allocation required to attempt first overload | |||
noisy delete # have to dealloc before stashing factory-generated pointer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before stashing factory-generted pointer
-> before considering factory init overload
Looks good to me (I left a couple of very minor cosmetic changes).
That was basically true for the previous implementation, too. The only extra overhead I see is that the deallocation gets called even if the new-style constructor isn't invokable with the given constructor arguments. But for that to matter, I think you'd have to do something like interspersing old and new (e.g. |
Yeah, seems unlikely. This reminds me: What do you think about adding a deprecation warning for the old-style constructors? (And eventually removing them in v3.0?). It should be possible to detect at module initialization time as |
I don't like the idea of a runtime deprecation warning; it has the nasty feature of annoying the end-user rather than just the module developer (unless it's something pretty serious like "this is definitely going to break in the next release"). Deprecating it in the changelog and removing it from the documentation is fine with me, though. The placement new stuff is currently still there, but I can't see anything that placement new would let you do anything you couldn't do with a (simpler) factory function constructor. |
On the other hand, a runtime warning when not under |
That sounds pretty good to me. Python's builtin warning handlers print messages only for the first unique appearance, so there would be a single warning per class instead of per |
Before deprecating placement-new, there was still one more place where it was being used that didn't have an alternative: // old
py::class<Foo>(m, "Foo")
...
.def("__getstate__", [](const Foo &self) {
return py::make_tuple(self.value1(), self.value2(), ...);
})
.def("__setstate__", [](Foo &self, py::tuple t) {
new (&self) Foo(t[0].cast<std::string>(), ...);
});
// new
py::class<Foo>(m, "Foo")
...
.def(py::pickle(
[](const Foo &self) { // __getstate__
return py::make_tuple(f.value1(), f.value2(), ...); // unchanged
},
[](py::tuple t) { // __setstate__, note: no `self` argument
return new Foo(t[0].cast<std::string>(), ...);
// or: return std::make_unique<Foo>(...); // return by holder
// or: return Foo(...); // return by value (move constructor)
}
));
|
This looks good to me. |
The lookup of the `self` type and value pointer are moved out of template code and into `dispatcher`. This brings down the binary size of constructors back to the level of the old placement-new approach. (It also avoids a second lookup for `init_instance`.) With this implementation, mixing old- and new-style constructors in the same overload set may result in some runtime overhead for temporary allocations/deallocations, but this should be fine as old style constructors are phased out.
I left out the 2 commits with pickling changes and I'll put them in a separate PR since they are not related to this and they introduce a significant change (sorry, should have done this initially). That also keeps the changelog entries clear. |
This is a follow-up to #805. It removes the binary size increase of the new-style constructors (about ~1% binary size reduction).
The lookup of the
self
type and value pointer are moved out of template code and intodispatcher
. This brings down the binary size of constructors back to the level of the old placement-new approach. (It also avoids a second lookup forinit_instance
.)With this implementation, mixing old- and new-style constructors in the same overload set may result in some runtime overhead for temporary allocations/deallocations, but this should be fine as
old style constructors are phased out.