diff options
author | Friedemann Kleint <[email protected]> | 2025-03-20 08:38:20 +0100 |
---|---|---|
committer | Friedemann Kleint <[email protected]> | 2025-05-22 15:34:18 +0200 |
commit | eeacd90bdb71cebcdfb8d285254d7e42ddc4ff79 (patch) | |
tree | be4a9fd0f99e7249c696afa5623ce3df4b3c880b /sources/shiboken6 | |
parent | 5587fe5f0a1179de1402440789c34ff87788abfa (diff) |
Speed up invocation of overridden functions
Cache the function found (extracted from the callable) instead of
doing lookups and MRO search each time. Use Py_None (unreferenced) to
indicate the method is not overridden. Reconstruct the callable from
the function.
Move the duckpunching check before the cache to avoid
reference issues.
Achieves a speedup of roughly 3.8->2.6s (33%).
Task-number: PYSIDE-2916
Change-Id: I6deec60d14ed7030284e0e02249ea96df49e5740
Reviewed-by: Cristian Maureira-Fredes <[email protected]>
Diffstat (limited to 'sources/shiboken6')
5 files changed, 60 insertions, 40 deletions
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 21e167b53..539094075 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -997,7 +997,7 @@ void CppGenerator::writeCacheResetNative(TextStream &s, const GeneratorContext & { s << "void " << classContext.wrapperName() << "::resetPyMethodCache()\n{\n" << indent - << "std::fill_n(m_PyMethodCache, sizeof(m_PyMethodCache) / sizeof(m_PyMethodCache[0]), false);\n" + << "std::fill(m_PyMethodCache.begin(), m_PyMethodCache.end(), nullptr);\n" << outdent << "}\n\n"; } diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp index 965d80dad..7616fa5c3 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.cpp +++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp @@ -185,6 +185,7 @@ void HeaderGenerator::writeWrapperClass(TextStream &s, for( const auto &includeGroup : includeGroups) s << includeGroup; } + s << "#include <sbkpython.h>\n\n#include <array>\n"; s << "namespace Shiboken { class AutoDecRef; class GilState; }\n\n"; @@ -288,9 +289,10 @@ void *qt_metacast(const char *_clname) override; } if (needsMethodCache) { - s << "mutable bool m_PyMethodCache[" << maxOverrides << "] = {false"; + s << "mutable std::array<PyObject *, " << maxOverrides + << "> m_PyMethodCache = {nullptr"; for (int i = 1; i < maxOverrides; ++i) - s << ", false"; + s << ", nullptr"; s << "};\n"; } diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index 3e26b4605..039cd3085 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -21,6 +21,7 @@ #include "voidptr.h" #include <algorithm> +#include <cctype> #include <cstddef> #include <cstring> #include <iostream> @@ -786,32 +787,57 @@ static PyObject *overrideMethodName(PyObject *pySelf, const char *methodName, // The virtual function call PyObject *Sbk_GetPyOverride(const void *voidThis, PyTypeObject *typeObject, Shiboken::GilState &gil, const char *funcName, - bool &resultCache, PyObject **nameCache) -{ - PyObject *pyOverride{}; - if (!resultCache) { - gil.acquire(); - auto &bindingManager = Shiboken::BindingManager::instance(); - SbkObject *wrapper = bindingManager.retrieveWrapper(voidThis, typeObject); - // The refcount can be 0 if the object is dieing and someone called - // a virtual method from the destructor - if (wrapper == nullptr) - return nullptr; - auto *pySelf = reinterpret_cast<PyObject *>(wrapper); - if (Py_REFCNT(pySelf) == 0) - return nullptr; - PyObject *pyMethodName = overrideMethodName(pySelf, funcName, nameCache); - pyOverride = Shiboken::BindingManager::getOverride(wrapper, pyMethodName); - if (pyOverride == nullptr) { - resultCache = true; - gil.release(); - } else if (Shiboken::Errors::occurred() != nullptr) { - // Give up. - Py_XDECREF(pyOverride); - pyOverride = nullptr; - } + PyObject *&resultCache, PyObject **nameCache) +{ + if (Py_IsInitialized() == 0 || resultCache == Py_None) + return nullptr; // Bail out, execute C++ call (wrappers may outlive Python). + + auto &bindingManager = Shiboken::BindingManager::instance(); + SbkObject *wrapper = bindingManager.retrieveWrapper(voidThis, typeObject); + // The refcount can be 0 if the object is dieing and someone called + // a virtual method from the destructor + if (wrapper == nullptr) + return nullptr; + auto *pySelf = reinterpret_cast<PyObject *>(wrapper); + if (Py_REFCNT(pySelf) == 0) + return nullptr; + + gil.acquire(); + + if (resultCache != nullptr) // recreate the callable from function/self + return PepExt_Type_CallDescrGet(resultCache, pySelf, nullptr); + + PyObject *pyMethodName = overrideMethodName(pySelf, funcName, nameCache); + auto *wrapper_dict = SbkObject_GetDict_NoRef(pySelf); + + // Note: This special case was implemented for duck-punching, which happens + // in the instance dict. It does not work with properties. + // This is not cached to avoid leaking. FIXME PYSIDE 7: Remove (PYSIDE-2916)? + if (PyObject *method = PyDict_GetItem(wrapper_dict, pyMethodName)) { + Py_INCREF(method); + return method; + } + + auto *pyOverride = Shiboken::BindingManager::getOverride(wrapper, pyMethodName); + if (pyOverride == nullptr) { + resultCache = Py_None; + Py_INCREF(resultCache); + gil.release(); + return nullptr; // No override, execute C++ call } - return pyOverride; + + if (Shiboken::Errors::occurred() != nullptr) { + // Give up. + Py_XDECREF(pyOverride); + resultCache = Py_None; + Py_INCREF(resultCache); + gil.release(); + return nullptr; // // Give up. + } + + resultCache = pyOverride; + // recreate the callable from function/self + return PepExt_Type_CallDescrGet(resultCache, pySelf, nullptr); } namespace diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h index 9eea89540..426298bcf 100644 --- a/sources/shiboken6/libshiboken/basewrapper.h +++ b/sources/shiboken6/libshiboken/basewrapper.h @@ -123,9 +123,8 @@ LIBSHIBOKEN_API PyObject *Sbk_ReturnFromPython_Self(PyObject *self); } // extern "C" LIBSHIBOKEN_API PyObject *Sbk_GetPyOverride(const void *voidThis, PyTypeObject *typeObject, - Shiboken::GilState &gil, - const char *funcName, bool &resultCache, - PyObject **nameCache); + Shiboken::GilState &gil, const char *funcName, + PyObject *&resultCache, PyObject **nameCache); namespace Shiboken { diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index c8874553f..ca509aefd 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -369,13 +369,6 @@ SbkObject *BindingManager::retrieveWrapper(const void *cptr, PyTypeObject *typeO PyObject *BindingManager::getOverride(SbkObject *wrapper, PyObject *pyMethodName) { auto *obWrapper = reinterpret_cast<PyObject *>(wrapper); - auto *wrapper_dict = SbkObject_GetDict_NoRef(obWrapper); - if (PyObject *method = PyDict_GetItem(wrapper_dict, pyMethodName)) { - // Note: This special case was implemented for duck-punching, which happens - // in the instance dict. It does not work with properties. - Py_INCREF(method); - return method; - } Shiboken::AutoDecRef method(PyObject_GetAttr(obWrapper, pyMethodName)); if (method.isNull()) @@ -413,13 +406,13 @@ PyObject *BindingManager::getOverride(SbkObject *wrapper, PyObject *pyMethodName if (PyObject *defaultMethod = PyDict_GetItem(parentDict.object(), pyMethodName)) { defaultFound = true; if (function != defaultMethod) - return method.release(); + return function; } } } // PYSIDE-2255: If no default method was found, use the method. if (!defaultFound) - return method.release(); + return function; return nullptr; } |