mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +00:00
Lazy instance value pointer allocation
We currently allocate instance values when creating the instance itself (except when constructing the instance for a `cast()`), but there is no particular reason to do so: the instance itself and the internals (for a non-simple layout) are allocated via Python, with no reason to expect better locality from the invoked `operator new`. Moreover, it makes implementation of factory function constructors trickier and slightly less efficient: they don't use the pre-eallocate the memory, which means there is a pointless allocation and free. This commit makes the allocation lazy: instead of preallocating when creating the instance, the allocation happens when the instance is first loaded (if null at that time). In addition to making it more efficient to deal with cases that don't need preallocation, this also allows for a very slight performance increase by not needing to look up the instances types during allocation. (There is a lookup during the eventual load, of course, but that is happening already).
This commit is contained in:
parent
8665ee8100
commit
fd81a03ec9
@ -560,7 +560,7 @@ inline PyThreadState *get_thread_state_unchecked() {
|
|||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
inline void keep_alive_impl(handle nurse, handle patient);
|
inline void keep_alive_impl(handle nurse, handle patient);
|
||||||
inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value = true);
|
inline PyObject *make_new_instance(PyTypeObject *type);
|
||||||
|
|
||||||
class type_caster_generic {
|
class type_caster_generic {
|
||||||
public:
|
public:
|
||||||
@ -591,7 +591,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type, false /* don't allocate value */));
|
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
|
||||||
auto wrapper = reinterpret_cast<instance *>(inst.ptr());
|
auto wrapper = reinterpret_cast<instance *>(inst.ptr());
|
||||||
wrapper->owned = false;
|
wrapper->owned = false;
|
||||||
void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();
|
void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();
|
||||||
@ -647,8 +647,14 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
|
|
||||||
// Base methods for generic caster; there are overridden in copyable_holder_caster
|
// Base methods for generic caster; there are overridden in copyable_holder_caster
|
||||||
void load_value(const value_and_holder &v_h) {
|
void load_value(value_and_holder &&v_h) {
|
||||||
value = v_h.value_ptr();
|
auto *&vptr = v_h.value_ptr();
|
||||||
|
// Lazy allocation for unallocated values:
|
||||||
|
if (vptr == nullptr) {
|
||||||
|
auto *type = v_h.type ? v_h.type : typeinfo;
|
||||||
|
vptr = type->operator_new(type->type_size);
|
||||||
|
}
|
||||||
|
value = vptr;
|
||||||
}
|
}
|
||||||
bool try_implicit_casts(handle src, bool convert) {
|
bool try_implicit_casts(handle src, bool convert) {
|
||||||
for (auto &cast : typeinfo->implicit_casts) {
|
for (auto &cast : typeinfo->implicit_casts) {
|
||||||
|
@ -232,11 +232,10 @@ inline bool deregister_instance(instance *self, void *valptr, const type_info *t
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instance creation function for all pybind11 types. It only allocates space for the C++ object
|
/// Instance creation function for all pybind11 types. It allocates the internal instance layout for
|
||||||
/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't
|
/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast
|
||||||
/// call the constructor -- an `__init__` function must do that (followed by an `init_instance`
|
/// to a reference or pointer), and initialization is done by an `__init__` function.
|
||||||
/// to set up the holder and register the instance).
|
inline PyObject *make_new_instance(PyTypeObject *type) {
|
||||||
inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) {
|
|
||||||
#if defined(PYPY_VERSION)
|
#if defined(PYPY_VERSION)
|
||||||
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
|
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
|
||||||
// object is a a plain Python type (i.e. not derived from an extension type). Fix it.
|
// object is a a plain Python type (i.e. not derived from an extension type). Fix it.
|
||||||
@ -251,13 +250,6 @@ inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= t
|
|||||||
inst->allocate_layout();
|
inst->allocate_layout();
|
||||||
|
|
||||||
inst->owned = true;
|
inst->owned = true;
|
||||||
// Allocate (if requested) the value pointers; otherwise leave them as nullptr
|
|
||||||
if (allocate_value) {
|
|
||||||
for (auto &v_h : values_and_holders(inst)) {
|
|
||||||
void *&vptr = v_h.value_ptr();
|
|
||||||
vptr = v_h.type->operator_new(v_h.type->type_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ struct instance {
|
|||||||
/// If true, get_internals().patients has an entry for this object
|
/// If true, get_internals().patients has an entry for this object
|
||||||
bool has_patients : 1;
|
bool has_patients : 1;
|
||||||
|
|
||||||
/// Initializes all of the above type/values/holders data
|
/// Initializes all of the above type/values/holders data (but not the instance values themselves)
|
||||||
void allocate_layout();
|
void allocate_layout();
|
||||||
|
|
||||||
/// Destroys/deallocates all of the above
|
/// Destroys/deallocates all of the above
|
||||||
|
Loading…
Reference in New Issue
Block a user