[Python-checkins] python/dist/src/Objects object.c,2.206,2.206.2.1 typeobject.c,2.220,2.220.2.1

gvanrossum@users.sourceforge.net gvanrossum@users.sourceforge.net
Tue, 25 Mar 2003 12:15:29 -0800


Update of /cvsroot/python/python/dist/src/Objects
In directory sc8-pr-cvs1:/tmp/cvs-serv2467

Modified Files:
      Tag: cache-attr-branch
	object.c typeobject.c 
Log Message:
(Experimental branch checkin.)
(Ping (mostly) & Guido)
Try to speed up instance attribute lookup by caching stuff.
Works fine, and is even faster in many cases, but is slower when the attribute
is found in the first class tried.

Index: object.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/object.c,v
retrieving revision 2.206
retrieving revision 2.206.2.1
diff -C2 -d -r2.206 -r2.206.2.1
*** object.c	23 Mar 2003 18:06:08 -0000	2.206
--- object.c	25 Mar 2003 20:15:15 -0000	2.206.2.1
***************
*** 1352,1362 ****
  }
  
  PyObject *
  PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
  {
  	PyTypeObject *tp = obj->ob_type;
  	PyObject *descr = NULL;
  	PyObject *res = NULL;
! 	descrgetfunc f;
  	long dictoffset;
  	PyObject **dictptr;
--- 1352,1446 ----
  }
  
+ #define GET_DESCR_FIELD(descr, field) \
+ 	(((descr) != NULL && \
+ 	  PyType_HasFeature((descr)->ob_type, Py_TPFLAGS_HAVE_CLASS)) ? \
+ 		(descr)->ob_type->field : NULL)
+ 
+ /* Find the dict where an attribute resides, and remember whether its value */
+ /* is a data descriptor.  The dict is returned un-INCREFed. */
+ PyObject *_PyObject_FindAttr(PyTypeObject *tp, PyObject *name,
+ 			     int *is_data_descr)
+ {
+ 	PyObject *pair = NULL;
+ 	PyObject *flag = NULL;
+ 	PyObject *where = NULL;
+ 	PyObject *descr = NULL;
+ 
+ 	if (tp->tp_cache != NULL) {
+ 		pair = PyDict_GetItem(tp->tp_cache, name);
+ 		/* pair is not owned by this func */
+ 		if (pair) {
+ 			flag = PyTuple_GET_ITEM(pair, 0);
+ 			where = PyTuple_GET_ITEM(pair, 1);
+ 			goto done;
+ 		}
+ 	}
+ 	
+ 	/* Inline _PyType_Lookup */
+ 	{
+ 		int i, n;
+ 		PyObject *mro, *base, *dict;
+ 
+ 		/* Look in tp_dict of types in MRO */
+ 		mro = tp->tp_mro;
+ 		assert(mro != NULL);
+ 		assert(PyTuple_Check(mro));
+ 		n = PyTuple_GET_SIZE(mro);
+ 		for (i = 0; i < n; i++) {
+ 			base = PyTuple_GET_ITEM(mro, i);
+ 			if (PyClass_Check(base)) {
+ 				dict = ((PyClassObject *) base)->cl_dict;
+ 			} else {
+ 				assert(PyType_Check(base));
+ 				dict = ((PyTypeObject *) base)->tp_dict;
+ 			}
+ 			assert(dict && PyDict_Check(dict));
+ 			descr = PyDict_GetItem(dict, name);
+ 			if (descr != NULL) {
+ 				if (GET_DESCR_FIELD(descr, tp_descr_set)) {
+ 					/* It's a data descriptor. */
+ 					flag = Py_True;
+ 				} else {
+ 					flag = Py_False;
+ 				}
+ 				where = dict;
+ 				break;
+ 			}
+ 		}
+ 	}
+ 
+ 	if (flag == NULL) {
+ 		flag = Py_False;
+ 		where = Py_None;
+ 	}
+ 
+ 	if (tp->tp_cache != NULL) {
+ 		pair = PyTuple_New(2);
+ 		Py_INCREF(flag);
+ 		PyTuple_SetItem(pair, 0, flag);
+ 		Py_INCREF(where);
+ 		PyTuple_SetItem(pair, 1, where);
+ 		PyDict_SetItem(tp->tp_cache, name, pair);
+ 		Py_DECREF(pair);
+ 	}
+ 
+   done:
+ 	*is_data_descr = (flag == Py_True);
+ 	if (where == Py_None) {
+ 		return NULL;
+ 	} else {
+ 		return where;
+ 	}
+ }
+ 
  PyObject *
  PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
  {
  	PyTypeObject *tp = obj->ob_type;
+ 	PyObject *where;
+ 	int is_data_descr = 0;
  	PyObject *descr = NULL;
  	PyObject *res = NULL;
! 	descrgetfunc descr_get;
  	long dictoffset;
  	PyObject **dictptr;
***************
*** 1388,1460 ****
  	}
  
! 	/* Inline _PyType_Lookup */
! 	{
! 		int i, n;
! 		PyObject *mro, *base, *dict;
! 
! 		/* Look in tp_dict of types in MRO */
! 		mro = tp->tp_mro;
! 		assert(mro != NULL);
! 		assert(PyTuple_Check(mro));
! 		n = PyTuple_GET_SIZE(mro);
! 		for (i = 0; i < n; i++) {
! 			base = PyTuple_GET_ITEM(mro, i);
! 			if (PyClass_Check(base))
! 				dict = ((PyClassObject *)base)->cl_dict;
! 			else {
! 				assert(PyType_Check(base));
! 				dict = ((PyTypeObject *)base)->tp_dict;
! 			}
! 			assert(dict && PyDict_Check(dict));
! 			descr = PyDict_GetItem(dict, name);
! 			if (descr != NULL)
! 				break;
! 		}
! 	}
  
! 	f = NULL;
! 	if (descr != NULL &&
! 	    PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {
! 		f = descr->ob_type->tp_descr_get;
! 		if (f != NULL && PyDescr_IsData(descr)) {
! 			res = f(descr, obj, (PyObject *)obj->ob_type);
! 			goto done;
! 		}
! 	}
  
! 	/* Inline _PyObject_GetDictPtr */
! 	dictoffset = tp->tp_dictoffset;
! 	if (dictoffset != 0) {
! 		PyObject *dict;
! 		if (dictoffset < 0) {
! 			int tsize;
! 			size_t size;
  
! 			tsize = ((PyVarObject *)obj)->ob_size;
! 			if (tsize < 0)
! 				tsize = -tsize;
! 			size = _PyObject_VAR_SIZE(tp, tsize);
  
! 			dictoffset += (long)size;
! 			assert(dictoffset > 0);
! 			assert(dictoffset % SIZEOF_VOID_P == 0);
! 		}
! 		dictptr = (PyObject **) ((char *)obj + dictoffset);
! 		dict = *dictptr;
! 		if (dict != NULL) {
! 			res = PyDict_GetItem(dict, name);
! 			if (res != NULL) {
! 				Py_INCREF(res);
! 				goto done;
  			}
  		}
  	}
  
! 	if (f != NULL) {
! 		res = f(descr, obj, (PyObject *)obj->ob_type);
! 		goto done;
! 	}
  
- 	if (descr != NULL) {
  		Py_INCREF(descr);
  		res = descr;
--- 1472,1520 ----
  	}
  
! 	/* Locate the attribute among the base classes. */
! 	where = _PyObject_FindAttr(tp, name, &is_data_descr);
  
! 	/* Data descriptor takes priority over instance attribute. */
! 	if (!is_data_descr) {
  
! 		/* Inline _PyObject_GetDictPtr */
! 		dictoffset = tp->tp_dictoffset;
! 		if (dictoffset != 0) {
! 			PyObject *dict;
! 			if (dictoffset < 0) {
! 				int tsize;
! 				size_t size;
  
! 				tsize = ((PyVarObject *)obj)->ob_size;
! 				if (tsize < 0)
! 					tsize = -tsize;
! 				size = _PyObject_VAR_SIZE(tp, tsize);
  
! 				dictoffset += (long)size;
! 				assert(dictoffset > 0);
! 				assert(dictoffset % SIZEOF_VOID_P == 0);
! 			}
! 			dictptr = (PyObject **) ((char *)obj + dictoffset);
! 			dict = *dictptr;
! 			if (dict != NULL) {
! 				res = PyDict_GetItem(dict, name);
! 				if (res != NULL) {
! 					Py_INCREF(res);
! 					goto done;
! 				}
  			}
  		}
  	}
  
! 	if (where != NULL) {
! 		descr = PyDict_GetItem(where, name);
! 		assert(descr != NULL);
! 		descr_get = GET_DESCR_FIELD(descr, tp_descr_get);
! 
! 		if (descr_get != NULL) {
! 			res = descr_get(descr, obj, (PyObject *) tp);
! 			goto done;
! 		}
  
  		Py_INCREF(descr);
  		res = descr;
***************
*** 1465,1468 ****
--- 1525,1529 ----
  		     "'%.50s' object has no attribute '%.400s'",
  		     tp->tp_name, PyString_AS_STRING(name));
+ 
    done:
  	Py_DECREF(name);
***************
*** 1474,1479 ****
  {
  	PyTypeObject *tp = obj->ob_type;
! 	PyObject *descr;
! 	descrsetfunc f;
  	PyObject **dictptr;
  	int res = -1;
--- 1535,1542 ----
  {
  	PyTypeObject *tp = obj->ob_type;
! 	PyObject *where;
! 	int is_data_descr = 0;
! 	PyObject *descr = NULL;
! 	descrsetfunc descr_set;
  	PyObject **dictptr;
  	int res = -1;
***************
*** 1505,1517 ****
  	}
  
! 	descr = _PyType_Lookup(tp, name);
! 	f = NULL;
! 	if (descr != NULL &&
! 	    PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {
! 		f = descr->ob_type->tp_descr_set;
! 		if (f != NULL && PyDescr_IsData(descr)) {
! 			res = f(descr, obj, value);
! 			goto done;
! 		}
  	}
  
--- 1568,1583 ----
  	}
  
! 	/* Locate the attribute among the base classes. */
! 	where = _PyObject_FindAttr(tp, name, &is_data_descr);
! 	if (where != NULL) {
! 		descr = PyDict_GetItem(where, name);
! 	}
! 	if (is_data_descr) {
! 		/* Data descriptor takes priority over instance attribute. */
! 		assert(descr != NULL);
! 		descr_set = GET_DESCR_FIELD(descr, tp_descr_set);
! 		assert(descr_set != NULL && PyDescr_IsData(descr));
! 		res = descr_set(descr, obj, value);
! 		goto done;
  	}
  
***************
*** 1536,1544 ****
  	}
  
- 	if (f != NULL) {
- 		res = f(descr, obj, value);
- 		goto done;
- 	}
- 
  	if (descr == NULL) {
  		PyErr_Format(PyExc_AttributeError,
--- 1602,1605 ----
***************
*** 1551,1554 ****
--- 1612,1616 ----
  		     "'%.50s' object attribute '%.400s' is read-only",
  		     tp->tp_name, PyString_AS_STRING(name));
+ 
    done:
  	Py_DECREF(name);

Index: typeobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/typeobject.c,v
retrieving revision 2.220
retrieving revision 2.220.2.1
diff -C2 -d -r2.220 -r2.220.2.1
*** typeobject.c	24 Mar 2003 23:49:49 -0000	2.220
--- typeobject.c	25 Mar 2003 20:15:21 -0000	2.220.2.1
***************
*** 179,184 ****
  type_set_bases(PyTypeObject *type, PyObject *value, void *context)
  {
! 	int i, r = 0;
  	PyObject *ob, *temp;
  	PyTypeObject *new_base, *old_base;
  	PyObject *old_bases, *old_mro;
--- 179,185 ----
  type_set_bases(PyTypeObject *type, PyObject *value, void *context)
  {
! 	int i, r = 0, cacheable;
  	PyObject *ob, *temp;
+ 	PyObject *ref, *subclass_list, *subclass;
  	PyTypeObject *new_base, *old_base;
  	PyObject *old_bases, *old_mro;
***************
*** 206,209 ****
--- 207,211 ----
  		return -1;
  	}
+ 
  	for (i = 0; i < PyTuple_GET_SIZE(value); i++) {
  		ob = PyTuple_GET_ITEM(value, i);
***************
*** 226,232 ****
  	new_base = best_base(value);
  
! 	if (!new_base) {
  		return -1;
- 	}
  
  	if (!compatible_for_assignment(type->tp_base, new_base, "__bases__"))
--- 228,233 ----
  	new_base = best_base(value);
  
! 	if (!new_base)
  		return -1;
  
  	if (!compatible_for_assignment(type->tp_base, new_base, "__bases__"))
***************
*** 243,251 ****
  	type->tp_base = new_base;
  
! 	if (mro_internal(type) < 0) {
  		goto bail;
- 	}
  
- 	temp = PyList_New(0);
  	if (!temp)
  		goto bail;
--- 244,250 ----
  	type->tp_base = new_base;
  
! 	if (mro_internal(type) < 0)
  		goto bail;
  
  	if (!temp)
  		goto bail;
***************
*** 269,272 ****
--- 268,312 ----
  	Py_DECREF(temp);
  
+ 	/* If there's a classic class among the bases, we can't use the cache */
+ 	cacheable = 1;
+ 	temp = type->tp_mro;
+ 	assert(PyTuple_Check(temp));
+ 	for (i = 0; i < PyTuple_GET_SIZE(temp); i++) {
+ 		PyObject *t = PyTuple_GET_ITEM(temp, i);
+ 		if (PyClass_Check(t)) {
+ 			cacheable = 0;
+ 			break;
+ 		}
+ 	}
+ 
+ 	/* Invalidate the cache for this type and all subclasses. */
+ 	Py_XDECREF(type->tp_cache);
+ 	if (cacheable)
+ 		type->tp_cache = PyDict_New();
+ 	else
+ 		type->tp_cache = NULL;
+ 
+ 	subclass_list = type->tp_subclasses;
+ 	if (subclass_list != NULL) {
+ 		assert(PyList_Check(subclass_list));
+ 		for (i = 0; i < PyList_GET_SIZE(subclass_list); i++) {
+ 			ref = PyList_GET_ITEM(subclass_list, i);
+ 			assert(PyWeakref_CheckRef(ref));
+ 			subclass = PyWeakref_GET_OBJECT(ref);
+ 			assert(subclass != NULL);
+ 			if (subclass != Py_None) {
+ 				PyTypeObject *sc;
+ 				assert(PyType_Check(subclass));
+ 				sc = (PyTypeObject *)subclass;
+ 				if (sc->tp_cache && cacheable) {
+ 					PyDict_Clear(sc->tp_cache);
+ 				} else {
+ 					Py_XDECREF(sc->tp_cache);
+ 					sc->tp_cache = NULL;
+ 				}
+ 			}
+ 		}
+ 	}
+ 
  	/* any base that was in __bases__ but now isn't, we
  	   need to remove |type| from its tp_subclasses.
***************
*** 323,326 ****
--- 363,376 ----
  
  static PyObject *
+ type_cache(PyTypeObject *type, void *context)
+ {
+ 	if (type->tp_cache == NULL) {
+ 		Py_INCREF(Py_None);
+ 		return Py_None;
+ 	}
+ 	return PyDictProxy_New(type->tp_cache);
+ }
+ 
+ static PyObject *
  type_get_doc(PyTypeObject *type, void *context)
  {
***************
*** 348,351 ****
--- 398,402 ----
  	{"__module__", (getter)type_module, (setter)type_set_module, NULL},
  	{"__dict__",  (getter)type_dict,  NULL, NULL},
+ 	{"__cache__",  (getter)type_cache,  NULL, NULL},
  	{"__doc__", (getter)type_get_doc, NULL, NULL},
  	{0}
***************
*** 1304,1309 ****
  		base_i = (PyTypeObject *)base_proto;
  		if (base_i->tp_dict == NULL) {
! 			if (PyType_Ready(base_i) < 0)
  				return NULL;
  		}
  		candidate = solid_base(base_i);
--- 1355,1361 ----
  		base_i = (PyTypeObject *)base_proto;
  		if (base_i->tp_dict == NULL) {
! 			if (PyType_Ready(base_i) < 0) {
  				return NULL;
+ 			}
  		}
  		candidate = solid_base(base_i);
***************
*** 1326,1332 ****
  		}
  	}
! 	if (base == NULL)
  		PyErr_SetString(PyExc_TypeError,
  			"a new-style class can't have only classic bases");
  	return base;
  }
--- 1378,1385 ----
  		}
  	}
! 	if (base == NULL) {
  		PyErr_SetString(PyExc_TypeError,
  			"a new-style class can't have only classic bases");
+ 	}
  	return base;
  }
***************
*** 2029,2034 ****
--- 2082,2108 ----
  
  static int
+ invalidate_cache(PyTypeObject *type, void *data)
+ {
+ 	PyObject *name = (PyObject *)data;
+ 	if (type->tp_cache != NULL) {
+ 		assert(PyDict_Check(type->tp_cache));
+ 		if (PyDict_DelItem(type->tp_cache, name) != 0) {
+ 			PyErr_Clear();
+ 		}
+ 	}
+ 	return 0;
+ }
+ 
+ #define GET_DESCR_FIELD(descr, field) \
+ 	(((descr) != NULL && \
+ 	  PyType_HasFeature((descr)->ob_type, Py_TPFLAGS_HAVE_CLASS)) ? \
+ 		(descr)->ob_type->field : NULL)
+ 
+ static int
  type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
  {
+ 	PyObject *old;
+ 	int was_data_descr, is_data_descr;
+ 
  	if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
  		PyErr_Format(
***************
*** 2038,2047 ****
  		return -1;
  	}
! 	/* XXX Example of how I expect this to be used...
! 	if (update_subclasses(type, name, invalidate_cache, NULL) < 0)
! 		return -1;
! 	*/
  	if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
  		return -1;
  	return update_slot(type, name);
  }
--- 2112,2128 ----
  		return -1;
  	}
! 
! 	/* If this change affects attribute lookup, invalidate cache entries. */
! 	old = PyDict_GetItem(type->tp_dict, name);
! 	was_data_descr = (old != NULL) && GET_DESCR_FIELD(old, tp_descr_set);
! 	is_data_descr = (value != NULL) && GET_DESCR_FIELD(value, tp_descr_set);
! 	if (was_data_descr != is_data_descr ||
! 	    (old == NULL) != (value == NULL)) {
! 		update_subclasses(type, name, invalidate_cache, name);
! 	}
! 
  	if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
  		return -1;
+ 
  	return update_slot(type, name);
  }
***************
*** 3053,3057 ****
  	PyObject *dict, *bases;
  	PyTypeObject *base;
! 	int i, n;
  
  	if (type->tp_flags & Py_TPFLAGS_READY) {
--- 3134,3138 ----
  	PyObject *dict, *bases;
  	PyTypeObject *base;
! 	int i, n, cacheable;
  
  	if (type->tp_flags & Py_TPFLAGS_READY) {
***************
*** 3140,3147 ****
  	assert(PyTuple_Check(bases));
  	n = PyTuple_GET_SIZE(bases);
  	for (i = 1; i < n; i++) {
  		PyObject *b = PyTuple_GET_ITEM(bases, i);
! 		if (PyType_Check(b))
  			inherit_slots(type, (PyTypeObject *)b);
  	}
  
--- 3221,3241 ----
  	assert(PyTuple_Check(bases));
  	n = PyTuple_GET_SIZE(bases);
+ 	cacheable = 1;
  	for (i = 1; i < n; i++) {
  		PyObject *b = PyTuple_GET_ITEM(bases, i);
! 		if (PyType_Check(b)) {
  			inherit_slots(type, (PyTypeObject *)b);
+ 		} else {
+ 			/* Cache only works if all bases are new-style. */
+ 			cacheable = 0;
+ 		}
+ 	}
+ 
+ 	/* Initialize the attribute location cache. */
+ 	Py_XDECREF(type->tp_cache);
+ 	if (cacheable) {
+ 		type->tp_cache = PyDict_New();
+ 	} else {
+ 		type->tp_cache = NULL;
  	}
  
***************
*** 3236,3240 ****
  		assert(PyWeakref_CheckRef(ref));
  		if (PyWeakref_GET_OBJECT(ref) == (PyObject*)type) {
! 			/* this can't fail, right? */
  			PySequence_DelItem(list, i);
  			return;
--- 3330,3334 ----
  		assert(PyWeakref_CheckRef(ref));
  		if (PyWeakref_GET_OBJECT(ref) == (PyObject*)type) {
! 			/* XXX What if the following fails? */
  			PySequence_DelItem(list, i);
  			return;