New submission from Neil Toronto:

This is a half-port of the patches in #1685986 and #1700288 to Python
3.0. Speedups are about the same as in those patches applied to their
respective Python versions for minibenchmarks (included in the patch as
fastattr_test_py3k.py): 5%-30% or more depending on how deep the class
hierarchy is. Pybench takes 4% less time on my system, and pystone takes
6% less time.

(Pybench would do better, but SpecialClassAttribute runs long and spends
half its time setting class attributes.)

The main difference between this and the previous patches is that 3.0's
simplifications allow a smaller footprint and make it easier to analyze
its correctness. Specifically, the fact that every object in the MRO
must be a type allows us to assume that every attribute lookup is
cacheable, and allows integration into the update_subclasses mechanism
when attributes are set. The lack of compiled extension modules means
there is no flag to check to see whether a type object supports caching.

----------
components: Interpreter Core
files: python30-attrcache.diff
messages: 58274
nosy: ntoronto
severity: normal
status: open
title: PATCH: Armin's attribute lookup caching for 3.0
type: rfe
versions: Python 3.0
Added file: http://bugs.python.org/file8889/python30-attrcache.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,97 @@
 
 #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.
+*/
+
+/* Note: defining this will cause some regression tests 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 */
+
+/* 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;
+}
+
 static PyMemberDef type_members[] = {
 	{"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY},
 	{"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY},
@@ -872,7 +963,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 +1390,7 @@
 		}
 	}
 	type->tp_mro = tuple;
+	type->tp_cache_id = next_cache_id++;
 	return 0;
 }
 
@@ -1428,7 +1520,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 +2113,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 +2142,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 +2268,14 @@
 			type->tp_name);
 		return -1;
 	}
-	/* XXX Example of how I expect this to be used...
-	if (update_subclasses(type, name, invalidate_cache, NULL) < 0)
-		return -1;
-	*/
+	if (PyUnicode_CheckExact(name)) {
+		namevalue nv;
+		nv.name = name;
+		nv.value = value;
+		if (update_subclasses(type, name,
+				type_attrcache_callback, &nv) < 0)
+			return -1;
+	}
 	if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
 		return -1;
 	return update_slot(type, name);
@@ -4295,7 +4430,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 +4835,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 +4967,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 +5446,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 +6078,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