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
This commit is contained in:
2011-01-06 04:01:06 +00:00
parent 1246f1c9b5
commit 68b931b03f

View File

@@ -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 int pyrna_prop_collection_bool( BPy_PropertyRNA *self )
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);
}
else {
PyErr_SetString(PyExc_RuntimeError, "error getting an rna struct from a collection");
if(keynum_abs < 0) {
PyErr_Format(PyExc_IndexError, "bpy_prop_collection[%d]: out of range.", keynum);
return NULL;
}
}
PyErr_Format(PyExc_IndexError, "bpy_prop_collection[index]: index %d out of range, size %d", keynum, len);
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_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;
}
}
static PyObject *pyrna_prop_array_subscript_int(BPy_PropertyArrayRNA *self, int keynum)
@@ -1455,27 +1470,36 @@ static PyObject *pyrna_prop_collection_subscript_str(BPy_PropertyRNA *self, cons
}
/* 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;
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_SetString(PyExc_RuntimeError, "error getting an rna struct from a collection");
return NULL;
}
if(start == 0 && stop == len) {
/* faster */
return pyrna_prop_collection_values(self);
}
else {
PointerRNA *ptr= &self->ptr;
PropertyRNA *prop= self->prop;
return list;
PointerRNA newptr;
PyObject *list = PyList_New(stop - start);
int count;
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;
}
}
return list;
}
}
/* TODO - dimensions
@@ -1568,21 +1592,31 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject
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 @@ static PyObject *pyrna_prop_array_subscript(BPy_PropertyArrayRNA *self, PyObject
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 @@ static PyGetSetDef pyrna_struct_getseters[] = {
{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 @@ static PyObject *pyrna_prop_keys(BPy_PropertyRNA *self)
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 @@ static PyObject *pyrna_prop_items(BPy_PropertyRNA *self)
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 @@ static PyObject *pyrna_struct_as_pointer(BPy_StructRNA *self)
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 @@ static void foreach_attr_type( BPy_PropertyRNA *self, char *attr,
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 @@ static PyObject *foreach_getset(BPy_PropertyRNA *self, PyObject *args, int set)
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 @@ PyObject *pyrna_prop_collection_iter(BPy_PropertyRNA *self)
/* 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_array_methods[] = {
};
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 @@ static PyObject *pyrna_basetype_dir(BPy_BaseTypeRNA *self)
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);