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

Reply via email to