From 056b0e63fb0401d1bc9bd0e6ff034943d38ab0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20K=C3=B6the?= Date: Fri, 17 Nov 2000 12:38:46 +0000 Subject: [PATCH] implemented the shared method table optimization [SVN r8239] --- class_wrapper.h | 5 + extclass_demo.cpp | 2 + module.cpp | 2 +- newtypes.cpp | 368 +++++++++++++++++++++++++++++++++++++++++++--- newtypes.h | 72 +++++++++ subclass.cpp | 2 + test_extclass.py | 4 + 7 files changed, 434 insertions(+), 21 deletions(-) diff --git a/class_wrapper.h b/class_wrapper.h index 9d3aa7fc..6ed3fa13 100644 --- a/class_wrapper.h +++ b/class_wrapper.h @@ -22,6 +22,11 @@ class ClassWrapper module.add(Ptr(as_object(m_class.get()), Ptr::new_ref), name); } + ~ClassWrapper() + { + m_class->share_method_tables(); + } + // define constructors template void def(const Signature& signature) diff --git a/extclass_demo.cpp b/extclass_demo.cpp index ff56299f..348cfae4 100644 --- a/extclass_demo.cpp +++ b/extclass_demo.cpp @@ -94,6 +94,7 @@ StringMapPythonClass::StringMapPythonClass() def(&get_item, "__getitem__"); def(&set_item, "__setitem__"); def(&del_item, "__delitem__"); + share_method_tables(); } int get_first(const IntPair& p) @@ -122,6 +123,7 @@ IntPairPythonClass::IntPairPythonClass() def(&get_first, "__getattr__first__"); def(&set_first, "__setattr__first__"); def(&del_first, "__delattr__first__"); + share_method_tables(); } void IntPairPythonClass::setattr(IntPair& x, const std::string& name, int value) diff --git a/module.cpp b/module.cpp index f4e9f91f..53fe1ec6 100644 --- a/module.cpp +++ b/module.cpp @@ -30,7 +30,7 @@ void Module::add(Ptr x, const char* name) void Module::add(PyTypeObject* x, const char* name /*= 0*/) { - this->add(Ptr(as_object(x), Ptr::new_ref), + this->add(Ptr(as_object(x)), name ? name : x->tp_name); } diff --git a/newtypes.cpp b/newtypes.cpp index 39cff270..a389d5da 100644 --- a/newtypes.cpp +++ b/newtypes.cpp @@ -398,7 +398,7 @@ namespace #define ENABLE_GENERAL_CAPABILITY(field) \ case TypeObjectBase::field: \ dest->tp_##field = &do_instance_##field; \ - break + return true bool add_capability_general(TypeObjectBase::Capability capability, PyTypeObject* dest) { @@ -416,16 +416,19 @@ bool add_capability_general(TypeObjectBase::Capability capability, PyTypeObject* default: return false; } - return true; } + template -void create_method_table_if_null(T *& table) +void create_method_table_if_null(T*& table) { if(table == 0) { - table = new T; - memset(table, 0, sizeof(T)); + detail::shared_pod_manager::instance().create(table); + } + else + { + detail::shared_pod_manager::instance().make_unique_copy(table); } } @@ -433,7 +436,7 @@ void create_method_table_if_null(T *& table) case TypeObjectBase::mapping_##field: \ create_method_table_if_null(dest); \ dest->mp_##field = &do_instance_mp_##field; \ - break + return true bool add_capability_mapping(TypeObjectBase::Capability capability, PyMappingMethods*& dest) { @@ -445,14 +448,13 @@ bool add_capability_mapping(TypeObjectBase::Capability capability, PyMappingMeth default: return false; } - return true; } #define ENABLE_SEQUENCE_CAPABILITY(field) \ case TypeObjectBase::sequence_##field: \ create_method_table_if_null(dest); \ dest->sq_##field = &do_instance_sq_##field; \ - break + return true bool add_capability_sequence(TypeObjectBase::Capability capability, PySequenceMethods*& dest) { @@ -468,14 +470,13 @@ bool add_capability_sequence(TypeObjectBase::Capability capability, PySequenceMe default: return false; } - return true; } #define ENABLE_NUMBER_CAPABILITY(field) \ case TypeObjectBase::number_##field: \ create_method_table_if_null(dest); \ dest->nb_##field = &do_instance_nb_##field; \ - break + return true bool add_capability_number(TypeObjectBase::Capability capability, PyNumberMethods*& dest) { @@ -507,14 +508,13 @@ bool add_capability_number(TypeObjectBase::Capability capability, PyNumberMethod default: return false; } - return true; } #define ENABLE_BUFFER_CAPABILITY(field) \ case TypeObjectBase::buffer_##field: \ create_method_table_if_null(dest); \ dest->bf_##field = &do_instance_bf_##field; \ - break + return true bool add_capability_buffer(TypeObjectBase::Capability capability, PyBufferProcs*& dest) { @@ -525,7 +525,6 @@ bool add_capability_buffer(TypeObjectBase::Capability capability, PyBufferProcs* default: return false; } - return true; } } // anonymous namespace @@ -552,17 +551,200 @@ namespace detail { } } // namespace detail +TypeObjectBase::~TypeObjectBase() +{ + detail::shared_pod_manager::instance().dispose(tp_as_mapping); + detail::shared_pod_manager::instance().dispose(tp_as_sequence); + detail::shared_pod_manager::instance().dispose(tp_as_number); + detail::shared_pod_manager::instance().dispose(tp_as_buffer); +} + void TypeObjectBase::enable(TypeObjectBase::Capability capability) { detail::add_capability(capability, this); } +void TypeObjectBase::share_method_tables() +{ + detail::shared_pod_manager::instance().replace_if_equal(tp_as_mapping); + detail::shared_pod_manager::instance().replace_if_equal(tp_as_sequence); + detail::shared_pod_manager::instance().replace_if_equal(tp_as_number); + detail::shared_pod_manager::instance().replace_if_equal(tp_as_buffer); +} + TypeObjectBase::TypeObjectBase(PyTypeObject* t) : PythonType(t) { this->tp_dealloc = do_instance_dealloc; } +namespace +{ + + +int* counted_pod_refcount(char* pod) +{ + if(pod == 0) + return 0; + return reinterpret_cast(pod - detail::shared_pod_manager::offset); +} + +#ifdef TYPE_OBJECT_BASE_STANDALONE_TEST +int pod_instance_counter = 0; +#endif + +int counted_pod_getref(char* pod) +{ + int* ref_count = counted_pod_refcount(pod); + if(ref_count == 0) + return -1; + return *ref_count; +} + +int counted_pod_decref(char* pod) +{ + int* ref_count = counted_pod_refcount(pod); + if(ref_count == 0) + return -1; + --(*ref_count); + if(*ref_count <= 0) + { +#ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + --pod_instance_counter; +#endif + ::operator delete(ref_count); + return 0; + } + return *ref_count; +} + +int counted_pod_incref(char* pod) +{ + int* ref_count = counted_pod_refcount(pod); + if(ref_count == 0) + return -1; + return ++(*ref_count); +} + +} // anonymous namespace + +namespace detail +{ + +struct shared_pod_manager::Compare +{ + bool operator()(const std::pair& x1, + const std::pair& x2) const + { + const std::size_t n1 = x1.second; + const std::size_t n2 = x2.second; + return n1 < n2 || n1 == n2 && PY_CSTD_::memcmp(x1.first, x2.first, n1) < 0; + } +}; + +struct shared_pod_manager::identical +{ + identical(char* p) : pod(p) {} + + bool operator()(const std::pair& x) const + { + return pod == x.first; + } + + char* pod; +}; + +shared_pod_manager& shared_pod_manager::instance() +{ + static shared_pod_manager spm; + return spm; +} + +shared_pod_manager::~shared_pod_manager() +{ +} + +void* shared_pod_manager::replace_if_equal(void* pod, std::size_t size) +{ + if(pod == 0) + return 0; + + const Holder element(static_cast(pod), size); + + const Storage::iterator found + = std::lower_bound(m_storage.begin(), m_storage.end(), element, Compare()); + + if (found != m_storage.end() && pod == found->first) + { + // pod already in list => do nothing + return pod; + } + else if (found != m_storage.end() && !Compare()(element, *found)) + { + // equal element in list => replace + void* replacement = found->first; + counted_pod_incref(found->first); + dec_ref(element.first); // invalidates iterator 'found' + return replacement; + } + else + { + // new element => insert + m_storage.insert(found, element); + return pod; + } +} + +void* shared_pod_manager::make_unique_copy(void* pod, std::size_t size) +{ + if(pod == 0) + return 0; + if(counted_pod_getref(static_cast(pod)) == 1) + return pod; + void* copy = create(size); + memmove(copy, pod, size); + dec_ref(pod); + return copy; +} + +void* shared_pod_manager::create(std::size_t size) +{ + std::size_t total_size = offset + size; + char* buffer = static_cast(::operator new(total_size)); +#ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + ++pod_instance_counter; +#endif + memset(buffer, 0, total_size); + + int* ref_count = reinterpret_cast(buffer); + *ref_count = 1; + + char* pod = buffer + offset; + + return pod; +} + +void shared_pod_manager::dec_ref(void* pod) +{ + if(pod == 0) + return; + + if(counted_pod_getref(static_cast(pod)) <= 1) + { + const Storage::iterator found = + std::find_if(m_storage.begin(), m_storage.end(), + identical(static_cast(pod))); + + if(found != m_storage.end()) + { + m_storage.erase(found); + } + } + counted_pod_decref(static_cast(pod)); +} + +} // namespace detail + namespace { struct ErrorType { operator PyObject*() const { return 0; } @@ -779,12 +961,158 @@ PyObject* TypeObjectBase::instance_number_hex(PyObject*) const return unimplemented("instance_number_hex"); } -TypeObjectBase::~TypeObjectBase() -{ - delete tp_as_mapping; - delete tp_as_sequence; - delete tp_as_number; - delete tp_as_buffer; -} } + +#ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + +struct TestTypeObject : py::TypeObjectBase +{ + TestTypeObject() + : py::TypeObjectBase(Py_None->ob_type->ob_type) + {} + + void instance_dealloc(PyObject*) const {} +}; + +int main() +{ + py::TypeObjectBase *o1, *o2, *o3; + + o1 = new TestTypeObject; + o2 = new TestTypeObject; + o3 = new TestTypeObject; + + assert(py::pod_instance_counter == 0); + + o1->enable(py::TypeObjectBase::number_add); + o1->enable(py::TypeObjectBase::compare); + + o2->enable(py::TypeObjectBase::number_add); + o2->enable(py::TypeObjectBase::mapping_length); + + o3->enable(py::TypeObjectBase::number_add); + o3->enable(py::TypeObjectBase::sequence_length); + + assert(py::pod_instance_counter == 5); + assert(o1->tp_as_number && !o1->tp_as_mapping && !o1->tp_as_sequence); + assert(o2->tp_as_number && o2->tp_as_mapping && !o2->tp_as_sequence); + assert(o3->tp_as_number && !o3->tp_as_mapping && o3->tp_as_sequence); + assert(o1->tp_as_number != o2->tp_as_number); + assert(o1->tp_as_number != o3->tp_as_number); + assert(o2->tp_as_number != o3->tp_as_number); + assert((void*)o2->tp_as_number != o2->tp_as_mapping); + assert((void*)o2->tp_as_mapping != o3->tp_as_sequence); + + o1->share_method_tables(); + o2->share_method_tables(); + o3->share_method_tables(); + + assert(py::pod_instance_counter == 3); + assert(o1->tp_as_number == o2->tp_as_number); + assert(o1->tp_as_number == o3->tp_as_number); + + o1->enable(py::TypeObjectBase::number_subtract); + + assert(py::pod_instance_counter == 4); + assert(o1->tp_as_number != o2->tp_as_number); + assert(o2->tp_as_number == o3->tp_as_number); + + o1->share_method_tables(); + + assert(py::pod_instance_counter == 4); + assert(o1->tp_as_number != o2->tp_as_number); + assert(o2->tp_as_number == o3->tp_as_number); + + o3->enable(py::TypeObjectBase::mapping_subscript); + + assert(py::pod_instance_counter == 5); + assert(o3->tp_as_number && o3->tp_as_mapping && o3->tp_as_sequence); + assert(o2->tp_as_mapping != o3->tp_as_mapping); + + o3->share_method_tables(); + + assert(py::pod_instance_counter == 5); + assert(o3->tp_as_number && o3->tp_as_mapping && o3->tp_as_sequence); + assert(o2->tp_as_mapping != o3->tp_as_mapping); + + o2->enable(py::TypeObjectBase::mapping_subscript); + o3->enable(py::TypeObjectBase::mapping_length); + + assert(py::pod_instance_counter == 5); + assert(o2->tp_as_number && o2->tp_as_mapping && !o2->tp_as_sequence); + assert(o3->tp_as_number && o3->tp_as_mapping && o3->tp_as_sequence); + assert(o2->tp_as_mapping != o3->tp_as_mapping); + + o2->share_method_tables(); + + assert(py::pod_instance_counter == 4); + assert(o2->tp_as_number && o2->tp_as_mapping && !o2->tp_as_sequence); + assert(o3->tp_as_number && o3->tp_as_mapping && o3->tp_as_sequence); + assert(o2->tp_as_mapping == o3->tp_as_mapping); + + py::TypeObjectBase *o4 = new TestTypeObject; + + assert(py::pod_instance_counter == 4); + + o4->enable(py::TypeObjectBase::number_add); + + assert(py::pod_instance_counter == 5); + assert(o4->tp_as_number && !o4->tp_as_mapping && !o4->tp_as_sequence); + + o4->share_method_tables(); + + assert(py::pod_instance_counter == 4); + assert(o4->tp_as_number && !o4->tp_as_mapping && !o4->tp_as_sequence); + assert(o4->tp_as_number == o3->tp_as_number); + + delete o3; + + assert(py::pod_instance_counter == 3); + assert(o1->tp_as_number && !o1->tp_as_mapping && !o1->tp_as_sequence); + assert(o2->tp_as_number && o2->tp_as_mapping && !o2->tp_as_sequence); + assert(o4->tp_as_number && !o4->tp_as_mapping && !o4->tp_as_sequence); + assert(o4->tp_as_number == o2->tp_as_number); + + o3 = new TestTypeObject; + + assert(py::pod_instance_counter == 3); + + o3->enable(py::TypeObjectBase::number_add); + o3->enable(py::TypeObjectBase::sequence_length); + + assert(py::pod_instance_counter == 5); + assert(o3->tp_as_number && !o3->tp_as_mapping && o3->tp_as_sequence); + assert(o1->tp_as_number != o3->tp_as_number); + assert(o2->tp_as_number != o3->tp_as_number); + assert((void*)o2->tp_as_number != o3->tp_as_sequence); + assert((void*)o2->tp_as_mapping != o3->tp_as_sequence); + + o3->share_method_tables(); + + assert(py::pod_instance_counter == 4); + assert(o3->tp_as_number && !o3->tp_as_mapping && o3->tp_as_sequence); + assert(o1->tp_as_number != o3->tp_as_number); + assert(o2->tp_as_number == o3->tp_as_number); + + delete o1; + + assert(py::pod_instance_counter == 3); + + delete o4; + + assert(py::pod_instance_counter == 3); + + delete o3; + + assert(py::pod_instance_counter == 2); + + delete o2; + + assert(py::pod_instance_counter == 0); + + assert(py::detail::shared_pod_manager::instance().m_storage.size() == 0); +} + +#endif + diff --git a/newtypes.h b/newtypes.h index 82eaf53a..f9710159 100644 --- a/newtypes.h +++ b/newtypes.h @@ -59,6 +59,8 @@ class TypeObjectBase : public PythonType void enable(Capability); + void share_method_tables(); + // // Type behaviors // @@ -297,6 +299,76 @@ PyObject* Reprable::instance_repr(PyObject* instance) const namespace detail { + class shared_pod_manager + { + struct counted_pod + { + union max_align { + short dummy0; + long dummy1; + double dummy2; + long double dummy3; + void* dummy4; + }; + + int m_ref_count; + max_align m_data; + }; + + typedef std::pair Holder; + typedef std::vector Storage; + + public: + static const int offset = offsetof(counted_pod, m_data); + + static shared_pod_manager& instance(); + ~shared_pod_manager(); + + template + void replace_if_equal(T*& t) + { + t = reinterpret_cast(replace_if_equal(t, sizeof(T))); + } + + template + void make_unique_copy(T*& t) + { + t = reinterpret_cast(make_unique_copy(t, sizeof(T))); + } + + template + void create(T*& t) + { + t = reinterpret_cast(create(sizeof(T))); + } + + template + void dispose(T* t) + { + dec_ref(t); + } + + private: + void* replace_if_equal(void* pod, std::size_t size); + void* make_unique_copy(void* pod, std::size_t size); + void* create(std::size_t size); + void dec_ref(void * pod); + + struct Compare; + struct identical; + + private: + shared_pod_manager() {} // singleton + +#ifdef TYPE_OBJECT_BASE_STANDALONE_TEST + public: +#endif + Storage m_storage; + }; + + + + void add_capability(TypeObjectBase::Capability capability, PyTypeObject* dest); diff --git a/subclass.cpp b/subclass.cpp index 172cd467..6c43ee5b 100644 --- a/subclass.cpp +++ b/subclass.cpp @@ -760,6 +760,8 @@ namespace { } } } + + derived->share_method_tables(); } void add_current_module_name(Dict& name_space) diff --git a/test_extclass.py b/test_extclass.py index 1bdcf6cf..d9194ffe 100644 --- a/test_extclass.py +++ b/test_extclass.py @@ -253,6 +253,10 @@ Special member functions in action >>> del df Deleting DerivedFromFoo + # force method table sharing + >>> class DerivedFromStringMap(StringMap): pass + ... + >>> m = StringMap() __getitem__()