Accessing __slots__ from C

Hrvoje Niksic hniksic at xemacs.org
Fri Sep 12 10:37:54 EDT 2008


Hrvoje Niksic <hniksic at xemacs.org> writes:

> Chris <ceball at users.sourceforge.net> writes:
>
>>> descr = GetAttrString(cls,"varname");
>>> offset = descr->d_member->offset;
>>> slotvar = (PyObject*)(((char*)obj)+offset)
>>
>> Unfortunately, I am inexperienced at this kind of thing, so I wasn't
>> able to get something working. Maybe someone could tell me what's
>> wrong with the code below (it gives the error "'struct _object' has no
>> member named 'd_member'")?
>
> You are getting that error because Carl forgot to cast the descriptor
> to the appropriate C type, in this case PyMemberDescrObject.  The last
> line is also incorrect, I think.  Try something like this:
>
>   PyObject *descr = PyObject_GetAttrString(x,"attr_one");

Also note that x should be your object's type, i.e. o->ob_type cast to
PyObject *.  The complete incantation would be:

// outside the loop
PyObject *descr = PyObject_GetAttrString((PyObject *) obj->ob_type,
                                         "attr_one");
if (!descr)
  return NULL;
int offset = ((PyMemberDescrObject *) descr)->d_member->offset;
Py_DECREF(descr);

// inside the loop
PyObject *value = *(PyObject **)((char *)obj + offset);
if (!value) {
  PyErr_SetString(PyExc_AttributeError, "attribute missing");
  return NULL;
}
// Remember to Py_INCREF(value) before using it and Py_DECREF it after
// using it.  Otherwise if the object changes its slot to something
// else, you might be using a dead object.

With this code, accessing the value is practically free once the
offset is calculated.  Here is a bench3 function updated to use this
strategy:

static PyObject *
bench3(PyObject *ignored, PyObject *obj)
{
  PyObject *descr = PyObject_GetAttrString((PyObject *) obj->ob_type,
                                           "abcdef");
  if (!descr)
    return NULL;

  // Here you should also assert that PyObject_TypeCheck(descr,
  // PyMemberDescr_Type) holds true.  Since PyMemberDescr_Type is static,
  // you'll have to get it by stashing away ob_type of a value known to be
  // the descriptor.  This is left as excercise to the reader.

  int offset = ((PyMemberDescrObject *) descr)->d_member->offset;
  Py_DECREF(descr);

  int i;
  PyObject *volatile attr;   // 'volatile' for benchmarking, prevents gcc
                             // from optimizing away the loop
  for (i = 0; i < 1000000; i++) {
    attr = *(PyObject **)((char *)obj + offset);
  }
  Py_INCREF(attr);
  return attr;        // to prove that we retrieved the correct value
}

>>> t0 = time.time(); attr.bench3(o); t1 = time.time()
1
>>> t1-t0
0.00071597099304199219       # for 1,000,000 iterations, as cheap as it gets



More information about the Python-list mailing list