summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorscoder <stefan_ml@behnel.de>2017-10-01 08:37:47 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2017-10-01 08:37:47 (GMT)
commit2102c789035ccacbac4362589402ac68baa2cd29 (patch)
tree952c4aba1d5994af985acdc7dba05e485ec03231 /Objects
parentd6bb65f378e34fe0c11fdb39588357ecf22964eb (diff)
downloadcpython-2102c789035ccacbac4362589402ac68baa2cd29.zip
cpython-2102c789035ccacbac4362589402ac68baa2cd29.tar.gz
cpython-2102c789035ccacbac4362589402ac68baa2cd29.tar.bz2
bpo-31336: Speed up type creation. (#3279)
Speed up class creation by 10-20% by reducing the overhead in the necessary special method lookups.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/typeobject.c160
1 files changed, 108 insertions, 52 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 5e0d81f..e72460b 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -2367,35 +2367,39 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
&bases, &PyDict_Type, &orig_dict))
return NULL;
- /* Determine the proper metatype to deal with this: */
- winner = _PyType_CalculateMetaclass(metatype, bases);
- if (winner == NULL) {
- return NULL;
- }
-
- if (winner != metatype) {
- if (winner->tp_new != type_new) /* Pass it to the winner */
- return winner->tp_new(winner, args, kwds);
- metatype = winner;
- }
-
/* Adjust for empty tuple bases */
nbases = PyTuple_GET_SIZE(bases);
if (nbases == 0) {
- bases = PyTuple_Pack(1, &PyBaseObject_Type);
+ base = &PyBaseObject_Type;
+ bases = PyTuple_Pack(1, base);
if (bases == NULL)
- goto error;
+ return NULL;
nbases = 1;
}
- else
- Py_INCREF(bases);
+ else {
+ /* Search the bases for the proper metatype to deal with this: */
+ winner = _PyType_CalculateMetaclass(metatype, bases);
+ if (winner == NULL) {
+ return NULL;
+ }
- /* Calculate best base, and check that all bases are type objects */
- base = best_base(bases);
- if (base == NULL) {
- goto error;
+ if (winner != metatype) {
+ if (winner->tp_new != type_new) /* Pass it to the winner */
+ return winner->tp_new(winner, args, kwds);
+ metatype = winner;
+ }
+
+ /* Calculate best base, and check that all bases are type objects */
+ base = best_base(bases);
+ if (base == NULL) {
+ return NULL;
+ }
+
+ Py_INCREF(bases);
}
+ /* Use "goto error" from this point on as we now own the reference to "bases". */
+
dict = PyDict_Copy(orig_dict);
if (dict == NULL)
goto error;
@@ -2945,25 +2949,23 @@ PyType_GetSlot(PyTypeObject *type, int slot)
return *(void**)(((char*)type) + slotoffsets[slot]);
}
-/* Internal API to look for a name through the MRO.
- This returns a borrowed reference, and doesn't set an exception! */
-PyObject *
-_PyType_Lookup(PyTypeObject *type, PyObject *name)
+/* Internal API to look for a name through the MRO, bypassing the method cache.
+ This returns a borrowed reference, and might set an exception.
+ 'error' is set to: -1: error with exception; 1: error without exception; 0: ok */
+static PyObject *
+find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
{
Py_ssize_t i, n;
PyObject *mro, *res, *base, *dict;
- unsigned int h;
+ Py_hash_t hash;
- if (MCACHE_CACHEABLE_NAME(name) &&
- PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
- /* fast path */
- h = MCACHE_HASH_METHOD(type, name);
- if (method_cache[h].version == type->tp_version_tag &&
- method_cache[h].name == name) {
-#if MCACHE_STATS
- method_cache_hits++;
-#endif
- return method_cache[h].value;
+ if (!PyUnicode_CheckExact(name) ||
+ (hash = ((PyASCIIObject *) name)->hash) == -1)
+ {
+ hash = PyObject_Hash(name);
+ if (hash == -1) {
+ *error = -1;
+ return NULL;
}
}
@@ -2971,28 +2973,22 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
mro = type->tp_mro;
if (mro == NULL) {
- if ((type->tp_flags & Py_TPFLAGS_READYING) == 0 &&
- PyType_Ready(type) < 0) {
- /* It's not ideal to clear the error condition,
- but this function is documented as not setting
- an exception, and I don't want to change that.
- When PyType_Ready() can't proceed, it won't
- set the "ready" flag, so future attempts to ready
- the same type will call it again -- hopefully
- in a context that propagates the exception out.
- */
- PyErr_Clear();
- return NULL;
+ if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) {
+ if (PyType_Ready(type) < 0) {
+ *error = -1;
+ return NULL;
+ }
+ mro = type->tp_mro;
}
- mro = type->tp_mro;
if (mro == NULL) {
+ *error = 1;
return NULL;
}
}
res = NULL;
- /* keep a strong reference to mro because type->tp_mro can be replaced
- during PyDict_GetItem(dict, name) */
+ /* Keep a strong reference to mro because type->tp_mro can be replaced
+ during dict lookup, e.g. when comparing to non-string keys. */
Py_INCREF(mro);
assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro);
@@ -3001,11 +2997,61 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
assert(PyType_Check(base));
dict = ((PyTypeObject *)base)->tp_dict;
assert(dict && PyDict_Check(dict));
- res = PyDict_GetItem(dict, name);
+ res = _PyDict_GetItem_KnownHash(dict, name, hash);
if (res != NULL)
break;
+ if (PyErr_Occurred()) {
+ *error = -1;
+ goto done;
+ }
}
+ *error = 0;
+done:
Py_DECREF(mro);
+ return res;
+}
+
+/* Internal API to look for a name through the MRO.
+ This returns a borrowed reference, and doesn't set an exception! */
+PyObject *
+_PyType_Lookup(PyTypeObject *type, PyObject *name)
+{
+ PyObject *res;
+ int error;
+ unsigned int h;
+
+ if (MCACHE_CACHEABLE_NAME(name) &&
+ PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
+ /* fast path */
+ h = MCACHE_HASH_METHOD(type, name);
+ if (method_cache[h].version == type->tp_version_tag &&
+ method_cache[h].name == name) {
+#if MCACHE_STATS
+ method_cache_hits++;
+#endif
+ return method_cache[h].value;
+ }
+ }
+
+ /* We may end up clearing live exceptions below, so make sure it's ours. */
+ assert(!PyErr_Occurred());
+
+ res = find_name_in_mro(type, name, &error);
+ /* Only put NULL results into cache if there was no error. */
+ if (error) {
+ /* It's not ideal to clear the error condition,
+ but this function is documented as not setting
+ an exception, and I don't want to change that.
+ E.g., when PyType_Ready() can't proceed, it won't
+ set the "ready" flag, so future attempts to ready
+ the same type will call it again -- hopefully
+ in a context that propagates the exception out.
+ */
+ if (error == -1) {
+ PyErr_Clear();
+ }
+ return NULL;
+ }
if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) {
h = MCACHE_HASH_METHOD(type, name);
@@ -6965,6 +7011,7 @@ update_one_slot(PyTypeObject *type, slotdef *p)
void *generic = NULL, *specific = NULL;
int use_generic = 0;
int offset = p->offset;
+ int error;
void **ptr = slotptr(type, offset);
if (ptr == NULL) {
@@ -6973,9 +7020,18 @@ update_one_slot(PyTypeObject *type, slotdef *p)
} while (p->offset == offset);
return p;
}
+ /* We may end up clearing live exceptions below, so make sure it's ours. */
+ assert(!PyErr_Occurred());
do {
- descr = _PyType_Lookup(type, p->name_strobj);
+ /* Use faster uncached lookup as we won't get any cache hits during type setup. */
+ descr = find_name_in_mro(type, p->name_strobj, &error);
if (descr == NULL) {
+ if (error == -1) {
+ /* It is unlikely by not impossible that there has been an exception
+ during lookup. Since this function originally expected no errors,
+ we ignore them here in order to keep up the interface. */
+ PyErr_Clear();
+ }
if (ptr == (void**)&type->tp_iternext) {
specific = (void *)_PyObject_NextNotImplemented;
}