[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [34117] trunk/blender/source/blender/ python/intern/bpy_rna.c: py/rna optimizations, will help for faster exporting.

Campbell Barton ideasman42 at gmail.com
Thu Jan 6 05:01:15 CET 2011


Revision: 34117
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=34117
Author:   campbellbarton
Date:     2011-01-06 05:01:06 +0100 (Thu, 06 Jan 2011)

Log Message:
-----------
py/rna optimizations, will help for faster exporting.

Speedup for getting collection indices, avoid getting the collection length unless a negative index is given. This avoids a loop over the entire collection in many cases.

Speedup for getting collection slices by detecting collection[:] and internally calling collection.values(), this gives a big speedup with some collections because each slice item would loop over the list until that index was found.

Rough test with 336 objects.
- getting index of listbase collection ~ 5.0x faster
- getting index of array collection ~ 1.15x faster

- getting slices of listbase collections ~ 34.0x faster
- getting slices of array collections ~ 1.5x faster

Modified Paths:
--------------
    trunk/blender/source/blender/python/intern/bpy_rna.c

Modified: trunk/blender/source/blender/python/intern/bpy_rna.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_rna.c	2011-01-06 03:10:41 UTC (rev 34116)
+++ trunk/blender/source/blender/python/intern/bpy_rna.c	2011-01-06 04:01:06 UTC (rev 34117)
@@ -57,6 +57,8 @@
 #define USE_MATHUTILS
 #define USE_STRING_COERCE
 
+static PyObject *pyrna_prop_collection_values(BPy_PropertyRNA *self);
+
 #ifdef USE_PEDANTIC_WRITE
 static short rna_disallow_writes= FALSE;
 
@@ -1414,21 +1416,34 @@
 static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_ssize_t keynum)
 {
 	PointerRNA newptr;
-	int len= RNA_property_collection_length(&self->ptr, self->prop);
+	Py_ssize_t keynum_abs= keynum;
 
-	if(keynum < 0) keynum += len;
+	/* notice getting the length of the collection is avoided unless negative index is used
+	 * or to detect internal error with a valid index.
+	 * This is done for faster lookups. */
+	if(keynum < 0) {
+		keynum_abs += RNA_property_collection_length(&self->ptr, self->prop);
 
-	if(keynum >= 0 && keynum < len)  {
-		if(RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum, &newptr)) {
-			return pyrna_struct_CreatePyObject(&newptr);
+		if(keynum_abs < 0) {
+			PyErr_Format(PyExc_IndexError, "bpy_prop_collection[%d]: out of range.", keynum);
+			return NULL;
 		}
+	}
+
+	if(RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) {
+		return pyrna_struct_CreatePyObject(&newptr);
+	}
+	else {
+		const int len= RNA_property_collection_length(&self->ptr, self->prop);
+		if(keynum_abs >= len) {
+			PyErr_Format(PyExc_IndexError, "bpy_prop_collection[index]: index %d out of range, size %d", keynum, len);
+		}
 		else {
-			PyErr_SetString(PyExc_RuntimeError, "error getting an rna struct from a collection");
-			return NULL;
+			PyErr_Format(PyExc_RuntimeError, "bpy_prop_collection[index]: internal error, valid index %d given in %d sized collection but value not found", keynum_abs, len);
 		}
+
+		return NULL;
 	}
-	PyErr_Format(PyExc_IndexError, "bpy_prop_collection[index]: index %d out of range, size %d", keynum, len);
-	return NULL;
 }
 
 static PyObject *pyrna_prop_array_subscript_int(BPy_PropertyArrayRNA *self, int keynum)
@@ -1455,27 +1470,36 @@
 }
 /* static PyObject *pyrna_prop_array_subscript_str(BPy_PropertyRNA *self, char *keyname) */
 
-static PyObject *pyrna_prop_collection_subscript_slice(PointerRNA *ptr, PropertyRNA *prop, int start, int stop)
+static PyObject *pyrna_prop_collection_subscript_slice(BPy_PropertyRNA *self, int start, int stop, int len)
 {
-	PointerRNA newptr;
-	PyObject *list = PyList_New(stop - start);
-	int count;
+	if(start == 0 && stop == len) {
+		/* faster */
+		return pyrna_prop_collection_values(self);
+	}
+	else {
+		PointerRNA *ptr= &self->ptr;
+		PropertyRNA *prop= self->prop;
 
-	start = MIN2(start,stop); /* values are clamped from  */
+		PointerRNA newptr;
+		PyObject *list = PyList_New(stop - start);
+		int count;
 
-	for(count = start; count < stop; count++) {
-		if(RNA_property_collection_lookup_int(ptr, prop, count - start, &newptr)) {
-			PyList_SET_ITEM(list, count - start, pyrna_struct_CreatePyObject(&newptr));
+		start = MIN2(start,stop); /* values are clamped from  */
+
+		for(count = start; count < stop; count++) {
+			if(RNA_property_collection_lookup_int(ptr, prop, count - start, &newptr)) {
+				PyList_SET_ITEM(list, count - start, pyrna_struct_CreatePyObject(&newptr));
+			}
+			else {
+				Py_DECREF(list);
+
+				PyErr_Format(PyExc_RuntimeError, "bpy_prop_collection[%d:%d]: internal error RNA_property_collection_lookup_int(...) failed with array index in range.", start, stop);
+				return NULL;
+			}
 		}
-		else {
-			Py_DECREF(list);
 
-			PyErr_SetString(PyExc_RuntimeError, "error getting an rna struct from a collection");
-			return NULL;
-		}
+		return list;
 	}
-
-	return list;
 }
 
 /* TODO - dimensions
@@ -1568,21 +1592,31 @@
 		return pyrna_prop_collection_subscript_int(self, i);
 	}
 	else if (PySlice_Check(key)) {
-		int len= RNA_property_collection_length(&self->ptr, self->prop);
-		Py_ssize_t start, stop, step, slicelength;
+		Py_ssize_t step= 1;
 
-		if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
+		if(((PySliceObject *)key)->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
 			return NULL;
-
-		if (slicelength <= 0) {
-			return PyList_New(0);
 		}
-		else if (step == 1) {
-			return pyrna_prop_collection_subscript_slice(&self->ptr, self->prop, start, stop);
+		else if (step != 1) {
+			PyErr_SetString(PyExc_TypeError, "bpy_prop_collection[slice]: slice steps not supported");
+			return NULL;
 		}
+		else if(((PySliceObject *)key)->start == Py_None && ((PySliceObject *)key)->stop == Py_None) {
+			return pyrna_prop_collection_values(self);
+		}
 		else {
-			PyErr_SetString(PyExc_TypeError, "bpy_prop_collection[slice]: slice steps not supported with rna");
-			return NULL;
+			int len= RNA_property_collection_length(&self->ptr, self->prop);
+			Py_ssize_t start, stop, slicelength;
+
+			if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
+				return NULL;
+
+			if (slicelength <= 0) {
+				return PyList_New(0);
+			}
+			else {
+				return pyrna_prop_collection_subscript_slice(self, start, stop, len);
+			}
 		}
 	}
 	else {
@@ -1603,21 +1637,33 @@
 		return pyrna_prop_array_subscript_int(self, PyLong_AsLong(key));
 	}
 	else if (PySlice_Check(key)) {
-		Py_ssize_t start, stop, step, slicelength;
-		int len = pyrna_prop_array_length(self);
+		Py_ssize_t step= 1;
 
-		if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
+		if(((PySliceObject *)key)->step != Py_None && !_PyEval_SliceIndex(key, &step)) {
 			return NULL;
-
-		if (slicelength <= 0) {
-			return PyList_New(0);
 		}
-		else if (step == 1) {
-			return pyrna_prop_array_subscript_slice(self, &self->ptr, self->prop, start, stop, len);
+		else if (step != 1) {
+			PyErr_SetString(PyExc_TypeError, "bpy_prop_array[slice]: slice steps not supported");
+			return NULL;
 		}
+		else if(((PySliceObject *)key)->start == Py_None && ((PySliceObject *)key)->stop == Py_None) {
+			/* note, no significant advantage with optimizing [:] slice as with collections but include here for consistency with collection slice func */
+			int len= pyrna_prop_array_length(self);
+			return pyrna_prop_array_subscript_slice(self, &self->ptr, self->prop, 0, len, len);
+		}
 		else {
-			PyErr_SetString(PyExc_TypeError, "bpy_prop_array[slice]: slice steps not supported with rna");
-			return NULL;
+			int len= pyrna_prop_array_length(self);
+			Py_ssize_t start, stop, slicelength;
+
+			if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
+				return NULL;
+
+			if (slicelength <= 0) {
+				return PyTuple_New(0);
+			}
+			else {
+				return pyrna_prop_array_subscript_slice(self, &self->ptr, self->prop, start, stop, len);
+			}
 		}
 	}
 	else {
@@ -3047,7 +3093,7 @@
 	{NULL,NULL,NULL,NULL,NULL}  /* Sentinel */
 };
 
-static PyObject *pyrna_prop_keys(BPy_PropertyRNA *self)
+static PyObject *pyrna_prop_collection_keys(BPy_PropertyRNA *self)
 {
 	PyObject *ret= PyList_New(0);
 	PyObject *item;
@@ -3072,7 +3118,7 @@
 	return ret;
 }
 
-static PyObject *pyrna_prop_items(BPy_PropertyRNA *self)
+static PyObject *pyrna_prop_collection_items(BPy_PropertyRNA *self)
 {
 	PyObject *ret= PyList_New(0);
 	PyObject *item;
@@ -3105,7 +3151,7 @@
 	return ret;
 }
 
-static PyObject *pyrna_prop_values(BPy_PropertyRNA *self)
+static PyObject *pyrna_prop_collection_values(BPy_PropertyRNA *self)
 {
 	PyObject *ret= PyList_New(0);
 	PyObject *item;
@@ -3174,7 +3220,7 @@
 	return PyLong_FromVoidPtr(self->ptr.data);
 }
 
-static PyObject *pyrna_prop_get(BPy_PropertyRNA *self, PyObject *args)
+static PyObject *pyrna_prop_collection_get(BPy_PropertyRNA *self, PyObject *args)
 {
 	PointerRNA newptr;
 	
@@ -3210,7 +3256,7 @@
 	RNA_PROP_END;
 }
 
-/* pyrna_prop_foreach_get/set both use this */
+/* pyrna_prop_collection_foreach_get/set both use this */
 static int foreach_parse_args(
 		BPy_PropertyRNA *self, PyObject *args,
 
@@ -3433,12 +3479,12 @@
 	Py_RETURN_NONE;
 }
 
-static PyObject *pyrna_prop_foreach_get(BPy_PropertyRNA *self, PyObject *args)
+static PyObject *pyrna_prop_collection_foreach_get(BPy_PropertyRNA *self, PyObject *args)
 {
 	return foreach_getset(self, args, 0);
 }
 
-static  PyObject *pyrna_prop_foreach_set(BPy_PropertyRNA *self, PyObject *args)
+static  PyObject *pyrna_prop_collection_foreach_set(BPy_PropertyRNA *self, PyObject *args)
 {
 	return foreach_getset(self, args, 1);
 }
@@ -3468,7 +3514,7 @@
 	/* Try get values from a collection */
 	PyObject *ret;
 	PyObject *iter= NULL;
-	ret = pyrna_prop_values(self);
+	ret= pyrna_prop_collection_values(self);
 	
 	/* we know this is a list so no need to PyIter_Check
 	 * otherwise it could be NULL (unlikely) if conversion failed */
@@ -3519,14 +3565,14 @@
 };
 
 static struct PyMethodDef pyrna_prop_collection_methods[] = {
-	{"foreach_get", (PyCFunction)pyrna_prop_foreach_get, METH_VARARGS, NULL},
-	{"foreach_set", (PyCFunction)pyrna_prop_foreach_set, METH_VARARGS, NULL},
+	{"foreach_get", (PyCFunction)pyrna_prop_collection_foreach_get, METH_VARARGS, NULL},
+	{"foreach_set", (PyCFunction)pyrna_prop_collection_foreach_set, METH_VARARGS, NULL},
 
-	{"keys", (PyCFunction)pyrna_prop_keys, METH_NOARGS, NULL},
-	{"items", (PyCFunction)pyrna_prop_items, METH_NOARGS,NULL},
-	{"values", (PyCFunction)pyrna_prop_values, METH_NOARGS, NULL},
+	{"keys", (PyCFunction)pyrna_prop_collection_keys, METH_NOARGS, NULL},
+	{"items", (PyCFunction)pyrna_prop_collection_items, METH_NOARGS,NULL},
+	{"values", (PyCFunction)pyrna_prop_collection_values, METH_NOARGS, NULL},
 	
-	{"get", (PyCFunction)pyrna_prop_get, METH_VARARGS, NULL},
+	{"get", (PyCFunction)pyrna_prop_collection_get, METH_VARARGS, NULL},
 	{NULL, NULL, 0, NULL}
 };
 
@@ -4797,7 +4843,7 @@
 	PyObject *list, *name;
 	PyMethodDef *meth;
 	
-	list= pyrna_prop_keys(self); /* like calling structs.keys(), avoids looping here */
+	list= pyrna_prop_collection_keys(self); /* like calling structs.keys(), avoids looping here */
 
 	for(meth=pyrna_basetype_methods; meth->ml_name; meth++) {
 		name = PyUnicode_FromString(meth->ml_name);





More information about the Bf-blender-cvs mailing list