diff options
| author | Christian Tismer <tismer@stackless.com> | 2020-07-25 00:58:18 +0200 |
|---|---|---|
| committer | Christian Tismer <tismer@stackless.com> | 2020-07-31 15:29:12 +0200 |
| commit | f2c973af4bd7cc1d87ec8ccfecadb7a475b723da (patch) | |
| tree | 7b94124fe5400e56215e3b20c0fbb134a46a8825 /sources/pyside2/libpyside/feature_select.cpp | |
| parent | edf28c5e97800b0c3442cd5351b6811ec85beead (diff) | |
feature-select: optimize feature access to the feasible maximum
** fix: MSVC needs extra sign bit in basewrapper_p.h! Buglet? **
The new feature selection has some tiny overhead.
It is about two dict accesses plus a slot access for every
tp_(get|set)attro call.
The introduction of an explicit `__init_feature__` call allows to
optimize this overhead very nicely, because this init is done for
each __feature__ import:
First, we can remove that tiny overhead completely by not initializing
the feature_select module at all if no __feature__ import is used.
Second, we can optimize this access further by caching the current module
dict. If the dict is unchanged, then the last select_id can be used.
This reduces the overhead of frequent calls to a single slot access.
Third, we finally cache the select id in unused SbkObjectType bits.
That removes the last structure where repeated attribute lookup is used.
The overhead is therefore quite small when something is changed.
But typically these changes are infrequent. The majority of accesses
do change nothing, and this case is now quite much optimized.
Change-Id: I7d1a4611a1c19216fd9be8f04457bb18ebd52ab1
Reviewed-by: Christian Tismer <tismer@stackless.com>
Diffstat (limited to 'sources/pyside2/libpyside/feature_select.cpp')
| -rw-r--r-- | sources/pyside2/libpyside/feature_select.cpp | 78 |
1 files changed, 62 insertions, 16 deletions
diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp index bc4461366..c703d18c4 100644 --- a/sources/pyside2/libpyside/feature_select.cpp +++ b/sources/pyside2/libpyside/feature_select.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "feature_select.h" +#include "pyside.h" #include <shiboken.h> #include <sbkstaticstrings.h> @@ -124,21 +125,35 @@ namespace PySide { namespace Feature { using namespace Shiboken; -static inline PyObject *getFeatureSelectID() +typedef bool(*FeatureProc)(PyTypeObject *type, PyObject *prev_dict); + +static FeatureProc *featurePointer = nullptr; + +static PyObject *cached_globals = nullptr; +static PyObject *last_select_id = nullptr; + +static PyObject *fast_id_array[256] = {}; + +static inline PyObject *getFeatureSelectId() { - static PyObject *zero = PyInt_FromLong(0); + static PyObject *zero = fast_id_array[0]; static PyObject *feature_dict = GetFeatureDict(); // these things are all borrowed PyObject *globals = PyEval_GetGlobals(); if (globals == nullptr) return zero; + if (globals == cached_globals) + return last_select_id; + PyObject *modname = PyDict_GetItem(globals, PyMagicName::name()); if (modname == nullptr) return zero; - PyObject *flag = PyDict_GetItem(feature_dict, modname); - if (flag == nullptr || !PyInt_Check(flag)) // int/long cheating + PyObject *select_id = PyDict_GetItem(feature_dict, modname); + if (select_id == nullptr || !PyInt_Check(select_id)) // int/long cheating return zero; - return flag; + cached_globals = globals; + last_select_id = select_id; + return select_id; } // Create a derived dict class @@ -193,6 +208,21 @@ static inline PyObject *getSelectId(PyObject *dict) return select_id; } +static inline void setCurrentSelectId(PyTypeObject *type, PyObject *select_id) +{ + SbkObjectType_SetReserved(type, PyInt_AsSsize_t(select_id)); // int/long cheating +} + +static inline void setCurrentSelectId(PyTypeObject *type, int id) +{ + SbkObjectType_SetReserved(type, id); +} + +static inline PyObject *getCurrentSelectId(PyTypeObject *type) +{ + return fast_id_array[SbkObjectType_GetReserved(type)]; +} + static bool replaceClassDict(PyTypeObject *type) { /* @@ -251,6 +281,7 @@ static bool moveToFeatureSet(PyTypeObject *type, PyObject *select_id) // This works because small numbers are singleton objects. if (current_id == select_id) { type->tp_dict = dict; + setCurrentSelectId(type, select_id); return true; } } while (dict != initial_dict); @@ -258,10 +289,6 @@ static bool moveToFeatureSet(PyTypeObject *type, PyObject *select_id) return false; } -typedef bool(*FeatureProc)(PyTypeObject *type, PyObject *prev_dict); - -static FeatureProc *featurePointer = nullptr; - static bool createNewFeatureSet(PyTypeObject *type, PyObject *select_id) { /* @@ -279,18 +306,19 @@ static bool createNewFeatureSet(PyTypeObject *type, PyObject *select_id) // make sure that small integers are cached assert(small_1 != nullptr && small_1 == small_2); - static auto zero = PyInt_FromLong(0); + static auto zero = fast_id_array[0]; bool ok = moveToFeatureSet(type, zero); Q_UNUSED(ok); assert(ok); AutoDecRef prev_dict(type->tp_dict); - Py_INCREF(prev_dict); + Py_INCREF(prev_dict); // keep the first ref unchanged if (!addNewDict(type, select_id)) return false; - auto id = PyInt_AsSsize_t(select_id); + auto id = PyInt_AsSsize_t(select_id); // int/long cheating if (id == -1) return false; + setCurrentSelectId(type, id); FeatureProc *proc = featurePointer; for (int idx = id; *proc != nullptr; ++proc, idx >>= 1) { if (idx & 1) { @@ -348,8 +376,8 @@ static inline PyObject *SelectFeatureSet(PyTypeObject *type) if (!replaceClassDict(type)) return nullptr; } - PyObject *select_id = getFeatureSelectID(); // borrowed - AutoDecRef current_id(getSelectId(type->tp_dict)); + PyObject *select_id = getFeatureSelectId(); // borrowed + PyObject *current_id = getCurrentSelectId(type); // borrowed if (select_id != current_id) { PyObject *mro = type->tp_mro; Py_ssize_t idx, n = PyTuple_GET_SIZE(mro); @@ -367,6 +395,8 @@ static inline PyObject *SelectFeatureSet(PyTypeObject *type) // For cppgenerator: void Select(PyObject *obj) { + if (featurePointer == nullptr) + return; auto type = Py_TYPE(obj); type->tp_dict = SelectFeatureSet(type); } @@ -392,10 +422,26 @@ static FeatureProc featureProcArray[] = { nullptr }; +void finalize() +{ + for (int idx = 0; idx < 256; ++idx) + Py_DECREF(fast_id_array[idx]); +} + void init() { - featurePointer = featureProcArray; - initSelectableFeature(SelectFeatureSet); + // This function can be called multiple times. + static bool is_initialized = false; + if (!is_initialized) { + for (int idx = 0; idx < 256; ++idx) + fast_id_array[idx] = PyInt_FromLong(idx); + featurePointer = featureProcArray; + initSelectableFeature(SelectFeatureSet); + registerCleanupFunction(finalize); + is_initialized = true; + } + // Reset the cache. This is called at any "from __feature__ import". + cached_globals = nullptr; } ////////////////////////////////////////////////////////////////////////////// |
