diff options
author | Travis Oliphant <oliphant@enthought.com> | 2006-04-24 20:44:33 +0000 |
---|---|---|
committer | Travis Oliphant <oliphant@enthought.com> | 2006-04-24 20:44:33 +0000 |
commit | 448bb44cca09075c0281f0857109db3925908098 (patch) | |
tree | d840339bb1f55de400b89db0c5170ca86c53ff34 /numpy/core | |
parent | 9867cad08e60ed9232a721dc9a95b7951455bfc1 (diff) | |
download | numpy-448bb44cca09075c0281f0857109db3925908098.tar.gz |
Fix so USE_USE_DEFAULTS code works in multi-threaded case. Speed up 1-d array indexing by an integer.
Diffstat (limited to 'numpy/core')
-rw-r--r-- | numpy/core/numeric.py | 15 | ||||
-rw-r--r-- | numpy/core/src/arrayobject.c | 31 | ||||
-rw-r--r-- | numpy/core/src/ufuncobject.c | 99 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 50 |
4 files changed, 99 insertions, 96 deletions
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index f57527990..557c8e7f4 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -395,13 +395,6 @@ def allclose (a, b, rtol=1.e-5, atol=1.e-8): return d.ravel().all() -class ufunc_values_obj(object): - def __init__(self, obj): - self._val_obj = obj - def __del__(self): - umath.seterrobj(self._val_obj) - del self._val_obj - _errdict = {"ignore":ERR_IGNORE, "warn":ERR_WARN, @@ -413,11 +406,11 @@ for key in _errdict.keys(): _errdict_rev[_errdict[key]] = key del key -def seterr(divide=None, over=None, under=None, invalid=None):
-
+def seterr(divide=None, over=None, under=None, invalid=None): + pyvals = umath.geterrobj() - old = geterr()
-
+ old = geterr() + if divide is None: divide = old['divide'] if over is None: over = old['over'] if under is None: under = old['under'] diff --git a/numpy/core/src/arrayobject.c b/numpy/core/src/arrayobject.c index 0bd14c251..d4b4bb83f 100644 --- a/numpy/core/src/arrayobject.c +++ b/numpy/core/src/arrayobject.c @@ -1468,10 +1468,22 @@ array_big_item(PyArrayObject *self, intp i) return (PyObject *)r; } +/* contains optimization for 1-d arrays */ static PyObject * array_item_nice(PyArrayObject *self, _int_or_ssize_t i) { - return PyArray_Return((PyArrayObject *)array_big_item(self, (intp) i)); + if (self->nd == 1) { + char *item; + if (i < 0) { + i += self->dimensions[0]; + } + if ((item = index2ptr(self, i)) == NULL) return NULL; + return PyArray_Scalar(item, self->descr, (PyObject *)self); + } + else { + return PyArray_Return((PyArrayObject *)\ + array_big_item(self, (intp) i)); + } } static int @@ -2344,7 +2356,22 @@ array_subscript_nice(PyArrayObject *self, PyObject *op) implementation may be possible by refactoring array_subscript */ - PyArrayObject *mp = (PyArrayObject *)array_subscript(self, op); + PyArrayObject *mp; + + /* optimization for integer select and 1-d */ + if (self->nd == 1 && (PyInt_Check(op) || PyLong_Check(op))) { + intp value; + char *item; + value = PyArray_PyIntAsIntp(op); + if (PyErr_Occurred()) + return NULL; + else if (value < 0) { + value += self->dimensions[0]; + } + if ((item = index2ptr(self, value)) == NULL) return NULL; + return PyArray_Scalar(item, self->descr, (PyObject *)self); + } + mp = (PyArrayObject *)array_subscript(self, op); if (mp == NULL) return NULL; diff --git a/numpy/core/src/ufuncobject.c b/numpy/core/src/ufuncobject.c index bc439b61c..40cf40735 100644 --- a/numpy/core/src/ufuncobject.c +++ b/numpy/core/src/ufuncobject.c @@ -35,9 +35,9 @@ typedef void (CdoubleBinaryFunc)(cdouble *x, cdouble *y, cdouble *res); typedef void (CfloatBinaryFunc)(cfloat *x, cfloat *y, cfloat *res); typedef void (ClongdoubleBinaryFunc)(clongdouble *x, clongdouble *y, \ clongdouble *res); -
-#define USE_USE_DEFAULTS 0
-
+ +#define USE_USE_DEFAULTS 1 + /*UFUNC_API*/ static void @@ -699,11 +699,11 @@ select_types(PyUFuncObject *self, int *arg_types, return 0; } -
-#if USE_USE_DEFAULTS -static int PyUFunc_USEDEFAULTS=0;
+ +#if USE_USE_DEFAULTS==1 +static int PyUFunc_NUM_NODEFAULTS=0; #endif -static PyObject *PyUFunc_PYVALS_NAME=NULL;
+static PyObject *PyUFunc_PYVALS_NAME=NULL; /*UFUNC_API*/ @@ -713,20 +713,21 @@ PyUFunc_GetPyValues(char *name, int *bufsize, int *errmask, PyObject **errobj) PyObject *thedict; PyObject *ref=NULL; PyObject *retval; -
- #if USE_USE_DEFAULTS - if (!PyUFunc_USEDEFAULTS) {
+ + #if USE_USE_DEFAULTS==1 + if (PyUFunc_NUM_NODEFAULTS != 0) { #endif if (PyUFunc_PYVALS_NAME == NULL) { - PyUFunc_PYVALS_NAME = PyString_InternFromString(UFUNC_PYVALS_NAME); + PyUFunc_PYVALS_NAME = \ + PyString_InternFromString(UFUNC_PYVALS_NAME); } thedict = PyThreadState_GetDict(); if (thedict == NULL) { thedict = PyEval_GetBuiltins(); } - ref = PyDict_GetItem(thedict, PyUFunc_PYVALS_NAME);
- #if USE_USE_DEFAULTS - }
+ ref = PyDict_GetItem(thedict, PyUFunc_PYVALS_NAME); + #if USE_USE_DEFAULTS==1 + } #endif if (ref == NULL) { *errmask = UFUNC_ERR_DEFAULT; @@ -2768,7 +2769,6 @@ ufunc_geterr(PyObject *dummy, PyObject *args) return res; } /* Construct list of defaults */ - fprintf(stderr, "Nothing found... return defaults.\n"); res = PyList_New(3); if (res == NULL) return NULL; PyList_SET_ITEM(res, 0, PyInt_FromLong(PyArray_BUFSIZE)); @@ -2776,43 +2776,39 @@ ufunc_geterr(PyObject *dummy, PyObject *args) PyList_SET_ITEM(res, 2, Py_None); Py_INCREF(Py_None); return res; } -
-#if USE_USE_DEFAULTS
-/*
-This doesn't look it will work in the presence of threads. It updates
-PyUFunc_USEDEFAULTS based on the current thread. If some other thread is
-around, it will see an incorrect value for use_defaults.
-
-I think the following strategy would fix this:
- 1. Change PyUFunc_USEDEFAULTS to PyUFunc_NONDEFAULTCOUNT or similar
- 2. Increment PyUFunc_NONDEFAULTCOUNT whenever a value is set to a nondefault
- value
- 3. Only use defaults when PyUFunc_NONDEFAULTCOUNT is nonzero.
-
-However, I'm not sure that it's worth the trouble. I've done a few small
-benchmarks and I see at most marginal speed improvements with the
-default values. So, for the time being, I'm simply ifdefing out the
-nonworking code and not worrying about it. If those benchmarks hold up, we
-should go ahead and rip the code out so as not to confuse future generations.
-
+ +#if USE_USE_DEFAULTS==1 +/* +This is a strategy to buy a little speed up and avoid the dictionary +look-up in the default case. It should work in the presence of +threads. If it is deemed too complicated or it doesn't actually work +it could be taken out. */ static int ufunc_update_use_defaults(void) { PyObject *errobj; int errmask, bufsize; - - PyUFunc_USEDEFAULTS = 0; - if (PyUFunc_GetPyValues("test", &bufsize, &errmask, &errobj) < 0) return -1; + int res; - if ((errmask == UFUNC_ERR_DEFAULT) && \ - (bufsize == PyArray_BUFSIZE) && \ - (PyTuple_GET_ITEM(errobj, 1) == Py_None)) { - PyUFunc_USEDEFAULTS = 1; + PyUFunc_NUM_NODEFAULTS += 1; + res = PyUFunc_GetPyValues("test", &bufsize, &errmask, + &errobj); + PyUFunc_NUM_NODEFAULTS -= 1; + + if (res < 0) return -1; + + if ((errmask != UFUNC_ERR_DEFAULT) || \ + (bufsize != PyArray_BUFSIZE) || \ + (PyTuple_GET_ITEM(errobj, 1) != Py_None)) { + PyUFunc_NUM_NODEFAULTS += 1; + } + else if (PyUFunc_NUM_NODEFAULTS > 0) { + PyUFunc_NUM_NODEFAULTS -= 1; } return 0; } -#endif
+#endif static PyObject * ufunc_seterr(PyObject *dummy, PyObject *args) @@ -2823,21 +2819,9 @@ ufunc_seterr(PyObject *dummy, PyObject *args) static char *msg = "Error object must be a list of length 3"; if (!PyArg_ParseTuple(args, "O", &val)) return NULL; - - if (!PyList_CheckExact(val)) { - PyObject *new; - new = PyObject_GetAttrString(val, "_val_obj"); - if (new == NULL) { - PyErr_SetString(PyExc_ValueError, msg); - return NULL; - } - val = new; - } - else Py_INCREF(val); if (!PyList_CheckExact(val) || PyList_GET_SIZE(val) != 3) { PyErr_SetString(PyExc_ValueError, msg); - Py_DECREF(val); return NULL; } if (PyUFunc_PYVALS_NAME == NULL) { @@ -2848,10 +2832,9 @@ ufunc_seterr(PyObject *dummy, PyObject *args) thedict = PyEval_GetBuiltins(); } res = PyDict_SetItem(thedict, PyUFunc_PYVALS_NAME, val); - Py_DECREF(val); - if (res < 0) return NULL;
-#if USE_USE_DEFAULTS - if (ufunc_update_use_defaults() < 0) return NULL;
+ if (res < 0) return NULL; +#if USE_USE_DEFAULTS==1 + if (ufunc_update_use_defaults() < 0) return NULL; #endif Py_INCREF(Py_None); return Py_None; diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 45204e3bf..000879b96 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -134,31 +134,31 @@ class test_bool_scalar(ScipyTestCase): self.failUnless((t ^ t) is f) self.failUnless((f ^ t) is t) self.failUnless((t ^ f) is t) - self.failUnless((f ^ f) is f)
-
- -class test_seterr(ScipyTestCase):
- def test_set(self):
- err = seterr()
- old = seterr(divide='warn')
- self.failUnless(err == old)
- new = seterr()
- self.failUnless(new['divide'] == 'warn')
- seterr(over='raise')
- self.failUnless(geterr()['over'] == 'raise')
- self.failUnless(new['divide'] == 'warn')
- seterr(**old)
- self.failUnless(geterr() == old)
- def test_divideerr(self):
- seterr(divide='raise')
- try:
- array([1.]) / array([0.])
- except FloatingPointError:
- pass
- else:
- self.fail()
- seterr(divide='ignore')
+ self.failUnless((f ^ f) is f) + + +class test_seterr(ScipyTestCase): + def test_set(self): + err = seterr() + old = seterr(divide='warn') + self.failUnless(err == old) + new = seterr() + self.failUnless(new['divide'] == 'warn') + seterr(over='raise') + self.failUnless(geterr()['over'] == 'raise') + self.failUnless(new['divide'] == 'warn') + seterr(**old) + self.failUnless(geterr() == old) + def test_divideerr(self): + seterr(divide='raise') + try: + array([1.]) / array([0.]) + except FloatingPointError: + pass + else: + self.fail() + seterr(divide='ignore') array([1.]) / array([0.]) -if __name__ == '__main__':
+if __name__ == '__main__': NumpyTest().run()
\ No newline at end of file |