Antoine Pitrou, 08.03.2012 21:36: > On Thu, 8 Mar 2012 14:36:06 -0600 > Benjamin Peterson wrote: >> 2012/3/8 Stefan Behnel: >>> Would that be acceptable for CPython as well or would you prefer full >>> fledged normalisation? >> >> I think we have to normalize for correctness. Consider that it may be >> some StopIteration subclass which set "value" on construction. > > Perhaps it would be time to drop the whole delayed normalization thing, > provided the benchmarks don't exhibit a slowdown?
At least for Cython, always normalising the exception can make quite a difference. For the nqueens benchmark, which uses short running generator expressions (not affected by this particular change), a quick hack to fetch, normalise and restore the StopIteration exception raised at the end of the generator expression run reduces the performance by 10% for me. I'd expect code similar to the group item iterator in itertools.groupby() to suffer even worse for very small groups. A while ago, I wouldn't have expected generator termination to have that an impact, but when we dropped the single frame+traceback creation at the end of the generator run in Cython, that boosted the performance of the compiled nqueens benchmark by 70% and a compiled Python version of itertools.groupby() ran twice as fast as before. These things can make an impressively large difference. http://thread.gmane.org/gmane.comp.python.cython.devel/12993/focus=13044 I'm using the following in Cython now. Note how complex the pre-3.3 case is, I'm sure that makes it even more worth the special case in older CPython versions (including 2.x). """ static int __Pyx_PyGen_FetchStopIterationValue(PyObject **pvalue) { PyObject *et, *ev, *tb; PyObject *value = NULL; if (PyErr_ExceptionMatches(PyExc_StopIteration)) { PyErr_Fetch(&et, &ev, &tb); // most common case: plain StopIteration without argument if (et == PyExc_StopIteration) { if (!ev || !PyObject_IsInstance(ev, PyExc_StopIteration)) { // PyErr_SetObject() puts the value directly into ev if (!ev) { Py_INCREF(Py_None); ev = Py_None; } Py_XDECREF(tb); Py_DECREF(et); *pvalue = ev; return 0; } } // otherwise: normalise and check what that gives us PyErr_NormalizeException(&et, &ev, &tb); if (PyObject_IsInstance(ev, PyExc_StopIteration)) { Py_XDECREF(tb); Py_DECREF(et); #if PY_VERSION_HEX >= 0x030300A0 value = ((PyStopIterationObject *)ev)->value; Py_INCREF(value); Py_DECREF(ev); #else PyObject* args = PyObject_GetAttrString(ev, "args"); Py_DECREF(ev); if (args) { value = PyObject_GetItem(args, 0); Py_DECREF(args); } if (!value) PyErr_Clear(); #endif } else { // looks like normalisation failed - raise the new exception PyErr_Restore(et, ev, tb); return -1; } } else if (PyErr_Occurred()) { return -1; } if (value == NULL) { Py_INCREF(Py_None); value = Py_None; } *pvalue = value; return 0; } """ Stefan _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com