Neil Toronto added the comment:
This patch adds the ability to invalidate all of a subclasses' cache
entries upon setattr rather than updating just one cache entry. It's not
clear which of these is the Right Thing To Do, so I've made it an #ifdef
for now. It defaults to updating.
Added file: http://bugs.python.org/file8891/python30-attrcache-1.diff
__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1568>
__________________________________
Index: Python/pythonrun.c
===================================================================
--- Python/pythonrun.c (revision 59400)
+++ Python/pythonrun.c (working copy)
@@ -501,6 +501,9 @@
/* Cleanup Unicode implementation */
_PyUnicode_Fini();
+
+ /* Report counts of attribute cache hits/misses (if enabled) */
+ PyType_Fini();
/* reset file system default encoding */
if (!Py_HasFileSystemDefaultEncoding) {
Index: Include/object.h
===================================================================
--- Include/object.h (revision 59400)
+++ Include/object.h (working copy)
@@ -373,6 +373,9 @@
PyObject *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
+ /* Unique cache ID per type, assigned when bases change (see
+ mro_internal) */
+ PY_LONG_LONG tp_cache_id;
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Index: Include/pythonrun.h
===================================================================
--- Include/pythonrun.h (revision 59400)
+++ Include/pythonrun.h (working copy)
@@ -141,6 +141,7 @@
PyAPI_FUNC(void) PyBytes_Fini(void);
PyAPI_FUNC(void) PyFloat_Fini(void);
PyAPI_FUNC(void) PyOS_FiniInterrupts(void);
+PyAPI_FUNC(void) PyType_Fini(void);
/* Stuff with no proper home (yet) */
PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *);
Index: fastattr_test_py3k.py
===================================================================
--- fastattr_test_py3k.py (revision 0)
+++ fastattr_test_py3k.py (revision 0)
@@ -0,0 +1,265 @@
+#!/usr/bin/python
+
+import timeit, random, time, sys
+
+MULTIPLIER = 1
+
+
+class A(object):
+ def __init__(self, *args):
+ pass
+
+class B(A): pass
+
+class C(B): pass
+
+class D(C): pass
+
+class E(D): pass
+
+class F(E): pass
+
+class G(F): pass
+
+class H(G):
+ def __init__(self):
+ pass
+
+class I(H): pass
+
+
+def test_init(tmp):
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+ tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__; tmp.__init__
+
+
+def test_class(tmp):
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+ tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__; tmp.__class__
+
+
+def test_has_init(tmp):
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+ hasattr(tmp, '__init__'); hasattr(tmp, '__init__')
+
+
+def test_has_class(tmp):
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+ hasattr(tmp, '__class__'); hasattr(tmp, '__class__')
+
+
+def test_has_intit(tmp):
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+ hasattr(tmp, '__intit__'); hasattr(tmp, '__intit__')
+
+
+def test_has_klass(tmp):
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+ hasattr(tmp, '__klass__'); hasattr(tmp, '__klass__')
+
+
+testclasses = [list, dict, tuple, A, B, C, D, E, F, G, H, I]
+list = list
+dict = dict
+tuple = tuple
+list_inst = list()
+dict_inst = dict()
+tuple_inst = tuple()
+A_inst = A()
+B_inst = B()
+C_inst = C()
+D_inst = D()
+E_inst = E()
+F_inst = F()
+G_inst = G()
+H_inst = H()
+I_inst = I()
+
+if __name__ == '__main__':
+ print('class class.__class__ class.__init__' +
+ ' class().__class__ class().__init__')
+ for cls in testclasses:
+ name = cls.__name__
+ print(name, end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_class(%s)' % name,
+ 'from __main__ import test_class, %s' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_init(%s)' % name,
+ 'from __main__ import test_init, %s' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_class(%s_inst)' % name,
+ 'from __main__ import test_class, %s_inst' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_init(%s_inst)' % name,
+ 'from __main__ import test_init, %s_inst' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print()
+ print()
+
+ print('class hasattr(class,"__class__") hasattr(class,"__init__")' +
+ ' hasattr(class(),"__class__") hasattr(class(),"__init__")')
+ for cls in testclasses:
+ name = cls.__name__
+ print(name, end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_class(%s)' % name,
+ 'from __main__ import test_has_class, %s' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_init(%s)' % name,
+ 'from __main__ import test_has_init, %s' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_class(%s_inst)' % name,
+ 'from __main__ import test_has_class, %s_inst' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_init(%s_inst)' % name,
+ 'from __main__ import test_has_init, %s_inst' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print()
+ print()
+
+ print('class hasattr(class,"__klass__") hasattr(class,"__intit__")' +
+ ' hasattr(class(),"__klass__") hasattr(class(),"__intit__")')
+ for cls in testclasses:
+ name = cls.__name__
+ print(name, end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_klass(%s)' % name,
+ 'from __main__ import test_has_klass, %s' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_intit(%s)' % name,
+ 'from __main__ import test_has_intit, %s' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_klass(%s_inst)' % name,
+ 'from __main__ import test_has_klass, %s_inst' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print(timeit.Timer('test_has_intit(%s_inst)' % name,
+ 'from __main__ import test_has_intit, %s_inst' % name)
+ .timeit(number=100000 * MULTIPLIER), end=' ')
+ sys.stdout.flush()
+
+ print()
+ print()
Property changes on: fastattr_test_py3k.py
___________________________________________________________________
Name: svn:executable
+ *
Index: Objects/object.c
===================================================================
--- Objects/object.c (revision 59400)
+++ Objects/object.c (working copy)
@@ -947,6 +947,7 @@
goto done;
}
+#if 0
/* Inline _PyType_Lookup */
{
Py_ssize_t i, n;
@@ -967,6 +968,9 @@
break;
}
}
+#else
+ descr = _PyType_Lookup(tp, name);
+#endif /* 0 */
Py_XINCREF(descr);
Index: Objects/typeobject.c
===================================================================
--- Objects/typeobject.c (revision 59400)
+++ Objects/typeobject.c (working copy)
@@ -6,6 +6,153 @@
#include <ctype.h>
+/*
+Attribute Lookup Caching
+
+This is implemented with an attribute cache that is dirt-simple for speed
+and global for good spatial locality. Per-type caches have been tried and
+didn't perform as well as this, and anything using standard dicts is
+almost guaranteed to be slower.
+
+Types now have a tp_cache_id, which uniquely identifies them to the global
+cache. This, along with the unicode hash, are combined to make an index
+into the cache. The cache is not fully associative: an entry will be kicked
+out if another attribute wants the same slot.
+
+Attributes can be set on a type only via setattr, which updates any cache
+entries for that attribute using the update_subclasses mechanism.
+
+Attribute names are compared by identity. Though this will give false
+negatives (possibly with computed names) it will never give false positives.
+Another way to say it is that name strings are assumed interned but it
+doesn't hurt us too much if they're not.
+*/
+
+/* Defining this will cause regression tests that check stdout to fail
+#define ATTRCACHE_COUNTS
+*/
+
+#ifdef ATTRCACHE_COUNTS
+static int attrcache_hits = 0;
+static int attrcache_misses = 0;
+#endif
+
+/* This should be used on lookups that are expected to be performed only
+once or infrequently (i.e. are expected to miss), such as slots lookups */
+static PyObject *_PyType_LookupInternal(PyTypeObject *type, PyObject *name);
+
+/* Cache is 2**ATTRCACHE_SIZE_EXP entries */
+#define ATTRCACHE_SIZE_EXP 13 /* 8192 entries */
+/* Too few entries (originally 1024) tends to slow down unrelated code
+paths such as long/long and float/float compares - don't know why -
+but this is probably a picky, architecture-dependent setting */
+
+/* Compute an index into the cache for a type and name hash. Low-order bits
+of tp_cache_id are multiplied to spread entries around. High-order bits
+are retained as the index for the same reason. */
+#define ATTRCACHE_INDEX(type, name_hash) \
+ (((unsigned int)(type)->tp_cache_id * (unsigned int)(name_hash)) \
+ >> (8*sizeof(unsigned int) - ATTRCACHE_SIZE_EXP))
+
+typedef struct {
+ PY_LONG_LONG tp_cache_id; /* from type->tp_cache_id */
+ PyObject *name; /* reference to unicode object or NULL */
+ PyObject *value; /* borrowed reference to attribute value or NULL */
+} attrcache_entry;
+
+/* The cache itself */
+static attrcache_entry attribute_cache[1 << ATTRCACHE_SIZE_EXP];
+/* The next new type or type that has its MRO changed gets this number as
+its tp_cache_id, then increments it. It would take at least a million
+years to overflow it at current processing speeds. */
+static PY_LONG_LONG next_cache_id = 1; /* start nonzero */
+
+/* It's still not clear whether, on setattr, it's best to
+1. Recurse through subclasses, updating cache values, and stopping recursion
+ before subclasses that shadow the attribute, or
+2. Just blindly invalidate the cache entries for every subclass.
+
+It really depends on real-world usage of class attributes. Define this to
+choose approach #2. Default is #1.
+
+#define ATTRCACHE_SETATTR_INVALIDATES
+*/
+
+#ifdef ATTRCACHE_SETATTR_INVALIDATES
+
+/* Called from type_setattro when an attribute is set. Invalidates all
+entries in the cache associated with type and all of its subclasses.
+Always returns 0. */
+int
+attrcache_invalidate_subclasses(PyTypeObject *type)
+{
+ PyTypeObject *subclass;
+ PyObject *ref, *subclasses;
+ Py_ssize_t i, n;
+
+ assert(PyUnicode_CheckExact(name));
+
+ type->tp_cache_id = next_cache_id++;
+
+ subclasses = type->tp_subclasses;
+ if (subclasses == NULL)
+ return 0;
+ assert(PyList_Check(subclasses));
+ n = PyList_GET_SIZE(subclasses);
+ for (i = 0; i < n; i++) {
+ ref = PyList_GET_ITEM(subclasses, i);
+ assert(PyWeakref_CheckRef(ref));
+ subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref);
+ assert(subclass != NULL);
+ if ((PyObject *)subclass == Py_None)
+ continue;
+ assert(PyType_Check(subclass));
+ if (attrcache_invalidate_subclasses(subclass) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+#else /* setattr updates */
+
+/* Bundle name and value for update_subclasses callback */
+typedef struct {
+ PyObject *name;
+ PyObject *value;
+} namevalue;
+
+/* An update_subclasses callback, called on type_setattro. Looks for a cache
+entry matching the type/name, and updates the value if it finds one. Returns
+0 on success, -1 on failure to hash the unicode object.
+
+update_subclasses stops recursing through subclasses when it finds a type
+with the name in its tp_dict. This is exactly right for us, since the
+subclass attribute shadows the one that was just set. */
+int
+type_attrcache_callback(PyTypeObject *type, void *data)
+{
+ register namevalue nv = *(namevalue *)data;
+ register attrcache_entry *ep;
+ long hash;
+
+ assert(PyUnicode_CheckExact(nv.name));
+
+ if ((hash = ((PyUnicodeObject *)nv.name)->hash) == -1) {
+ hash = PyObject_Hash(nv.name);
+ if (hash == -1) /* Needed? Unicode hash can't fail */
+ return -1;
+ }
+ ep = &attribute_cache[ATTRCACHE_INDEX(type, hash)];
+ if (ep->tp_cache_id == type->tp_cache_id &&
+ ep->name == nv.name) {
+ ep->value = nv.value; /* borrowed */
+ }
+
+ return 0;
+}
+
+#endif /* #ifdef ATTRCACHE_SETATTR_INVALIDATES */
+
static PyMemberDef type_members[] = {
{"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY},
{"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY},
@@ -872,7 +1019,7 @@
if (*attrobj == NULL)
return NULL;
}
- res = _PyType_Lookup(Py_Type(self), *attrobj);
+ res = _PyType_LookupInternal(Py_Type(self), *attrobj);
if (res != NULL) {
descrgetfunc f;
if ((f = Py_Type(res)->tp_descr_get) == NULL)
@@ -1299,6 +1446,7 @@
}
}
type->tp_mro = tuple;
+ type->tp_cache_id = next_cache_id++;
return 0;
}
@@ -1428,7 +1576,7 @@
if (dict_str == NULL)
return NULL;
}
- descr = _PyType_Lookup(type, dict_str);
+ descr = _PyType_LookupInternal(type, dict_str);
if (descr == NULL || !PyDescr_IsData(descr))
return NULL;
@@ -2021,8 +2169,8 @@
/* 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)
+static PyObject *
+_PyType_LookupInternal(PyTypeObject *type, PyObject *name)
{
Py_ssize_t i, n;
PyObject *mro, *res, *base, *dict;
@@ -2050,6 +2198,45 @@
return NULL;
}
+/* Looks up an attribute in the MRO. Returns NULL on failure or not-found,
+never sets an exception, otherwise returns a borrowed reference. This
+caches lookups, including failed ones. Use _PyType_LookupInternal instead
+for lookups that are done once or infrequently, such as slots. */
+PyObject *
+_PyType_Lookup(PyTypeObject *type, register PyObject *name)
+{
+ register attrcache_entry *ep;
+ long hash;
+
+ if (!PyUnicode_CheckExact(name))
+ return _PyType_LookupInternal(type, name);
+
+ if ((hash = ((PyUnicodeObject *)name)->hash) == -1) {
+ hash = PyObject_Hash(name);
+ if (hash == -1) /* Needed? Unicode hash can't fail */
+ return NULL;
+ }
+ ep = &attribute_cache[ATTRCACHE_INDEX(type, hash)];
+ if (ep->tp_cache_id == type->tp_cache_id &&
+ ep->name == name) {
+#ifdef ATTRCACHE_COUNTS
+ attrcache_hits++;
+#endif
+ assert(ep->value == _PyType_LookupInternal(type, name));
+ return ep->value;
+ }
+#ifdef ATTRCACHE_COUNTS
+ attrcache_misses++;
+#endif
+
+ ep->tp_cache_id = type->tp_cache_id;
+ Py_XDECREF(ep->name);
+ Py_INCREF(name);
+ ep->name = name;
+ ep->value = _PyType_LookupInternal(type, name); /* borrowed */
+ return ep->value;
+}
+
/* This is similar to PyObject_GenericGetAttr(),
but uses _PyType_Lookup() instead of just looking in type->tp_dict. */
static PyObject *
@@ -2137,10 +2324,19 @@
type->tp_name);
return -1;
}
- /* XXX Example of how I expect this to be used...
- if (update_subclasses(type, name, invalidate_cache, NULL) < 0)
+#ifdef ATTRCACHE_SETATTR_INVALIDATES
+ if (attrcache_invalidate_subclasses(type) < 0)
return -1;
- */
+#else
+ if (PyUnicode_CheckExact(name)) {
+ namevalue nv;
+ nv.name = name;
+ nv.value = value;
+ if (update_subclasses(type, name,
+ type_attrcache_callback, &nv) < 0)
+ return -1;
+ }
+#endif /* #ifdef ATTRCACHE_SETATTR_INVALIDATES */
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
return -1;
return update_slot(type, name);
@@ -4295,7 +4491,7 @@
if (getitem_str == NULL)
return NULL;
}
- func = _PyType_Lookup(Py_Type(self), getitem_str);
+ func = _PyType_LookupInternal(Py_Type(self), getitem_str);
if (func != NULL) {
if ((f = Py_Type(func)->tp_descr_get) == NULL)
Py_INCREF(func);
@@ -4700,13 +4896,13 @@
if (getattribute_str == NULL)
return NULL;
}
- getattr = _PyType_Lookup(tp, getattr_str);
+ getattr = _PyType_LookupInternal(tp, getattr_str);
if (getattr == NULL) {
/* No __getattr__ hook: use a simpler dispatcher */
tp->tp_getattro = slot_tp_getattro;
return slot_tp_getattro(self, name);
}
- getattribute = _PyType_Lookup(tp, getattribute_str);
+ getattribute = _PyType_LookupInternal(tp, getattribute_str);
if (getattribute == NULL ||
(Py_Type(getattribute) == &PyWrapperDescr_Type &&
((PyWrapperDescrObject *)getattribute)->d_wrapped ==
@@ -4832,7 +5028,7 @@
if (get_str == NULL)
return NULL;
}
- get = _PyType_Lookup(tp, get_str);
+ get = _PyType_LookupInternal(tp, get_str);
if (get == NULL) {
/* Avoid further slowdowns */
if (tp->tp_descr_get == slot_tp_descr_get)
@@ -5311,7 +5507,7 @@
return p;
}
do {
- descr = _PyType_Lookup(type, p->name_strobj);
+ descr = _PyType_LookupInternal(type, p->name_strobj);
if (descr == NULL)
continue;
if (Py_Type(descr) == &PyWrapperDescr_Type) {
@@ -5943,3 +6139,13 @@
PyType_GenericNew, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
+
+void
+PyType_Fini(void)
+{
+#ifdef ATTRCACHE_COUNTS
+ printf("Attribute cache hits: %d, misses: %d, percent hit: %.2f\n",
+ attrcache_hits, attrcache_misses,
+ 100.0*attrcache_hits/(attrcache_hits+attrcache_misses));
+#endif
+}
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com