[pypy-commit] pypy default: Merged in scalar-operations (pull request #243)
rlamy
noreply at buildbot.pypy.org
Sun Jul 6 15:41:11 CEST 2014
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch:
Changeset: r72369:f1bd7e48eb65
Date: 2014-07-06 14:40 +0100
http://bitbucket.org/pypy/pypy/changeset/f1bd7e48eb65/
Log: Merged in scalar-operations (pull request #243)
Fix performance regression on ufunc(<scalar>, <scalar>) in numpy
diff --git a/pypy/module/micronumpy/base.py b/pypy/module/micronumpy/base.py
--- a/pypy/module/micronumpy/base.py
+++ b/pypy/module/micronumpy/base.py
@@ -18,7 +18,12 @@
pass
-class W_NDimArray(W_Root):
+class W_NumpyObject(W_Root):
+ """Base class for ndarrays and scalars (aka boxes)."""
+ _attrs_ = []
+
+
+class W_NDimArray(W_NumpyObject):
__metaclass__ = extendabletype
def __init__(self, implementation):
@@ -85,6 +90,14 @@
w_val = dtype.coerce(space, space.wrap(0))
return convert_to_array(space, w_val)
+ @staticmethod
+ def from_scalar(space, w_scalar):
+ """Convert a scalar into a 0-dim array"""
+ dtype = w_scalar.get_dtype(space)
+ w_arr = W_NDimArray.from_shape(space, [], dtype)
+ w_arr.set_scalar_value(w_scalar)
+ return w_arr
+
def convert_to_array(space, w_obj):
from pypy.module.micronumpy.ctors import array
diff --git a/pypy/module/micronumpy/boxes.py b/pypy/module/micronumpy/boxes.py
--- a/pypy/module/micronumpy/boxes.py
+++ b/pypy/module/micronumpy/boxes.py
@@ -1,4 +1,3 @@
-from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.mixedmodule import MixedModule
@@ -14,7 +13,7 @@
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.tool.sourcetools import func_with_new_name
from pypy.module.micronumpy import constants as NPY
-from pypy.module.micronumpy.base import W_NDimArray
+from pypy.module.micronumpy.base import W_NDimArray, W_NumpyObject
from pypy.module.micronumpy.concrete import VoidBoxStorage
from pypy.module.micronumpy.flagsobj import W_FlagsObject
@@ -126,7 +125,7 @@
return ret
-class W_GenericBox(W_Root):
+class W_GenericBox(W_NumpyObject):
_attrs_ = ['w_flags']
def descr__new__(space, w_subtype, __args__):
@@ -136,6 +135,12 @@
def get_dtype(self, space):
return self._get_dtype(space)
+ def is_scalar(self):
+ return True
+
+ def get_scalar_value(self):
+ return self
+
def item(self, space):
return self.get_dtype(space).itemtype.to_builtin_type(space, self)
diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py
--- a/pypy/module/micronumpy/ctors.py
+++ b/pypy/module/micronumpy/ctors.py
@@ -4,7 +4,8 @@
from rpython.rlib.rstring import strip_spaces
from rpython.rtyper.lltypesystem import lltype, rffi
from pypy.module.micronumpy import descriptor, loop
-from pypy.module.micronumpy.base import W_NDimArray, convert_to_array
+from pypy.module.micronumpy.base import (
+ W_NDimArray, convert_to_array, W_NumpyObject)
from pypy.module.micronumpy.converters import shape_converter
@@ -24,24 +25,44 @@
return box
+def try_array_method(space, w_object, w_dtype=None):
+ w___array__ = space.lookup(w_object, "__array__")
+ if w___array__ is None:
+ return None
+ if w_dtype is None:
+ w_dtype = space.w_None
+ w_array = space.get_and_call_function(w___array__, w_object, w_dtype)
+ if isinstance(w_array, W_NDimArray):
+ return w_array
+ else:
+ raise oefmt(space.w_ValueError,
+ "object __array__ method not producing an array")
+
+
@unwrap_spec(ndmin=int, copy=bool, subok=bool)
def array(space, w_object, w_dtype=None, copy=True, w_order=None, subok=False,
ndmin=0):
+ w_res = _array(space, w_object, w_dtype, copy, w_order, subok)
+ shape = w_res.get_shape()
+ if len(shape) < ndmin:
+ shape = [1] * (ndmin - len(shape)) + shape
+ impl = w_res.implementation.set_shape(space, w_res, shape)
+ if w_res is w_object:
+ return W_NDimArray(impl)
+ else:
+ w_res.implementation = impl
+ return w_res
+
+def _array(space, w_object, w_dtype=None, copy=True, w_order=None, subok=False):
from pypy.module.micronumpy import strides
# for anything that isn't already an array, try __array__ method first
if not isinstance(w_object, W_NDimArray):
- w___array__ = space.lookup(w_object, "__array__")
- if w___array__ is not None:
- if space.is_none(w_dtype):
- w_dtype = space.w_None
- w_array = space.get_and_call_function(w___array__, w_object, w_dtype)
- if isinstance(w_array, W_NDimArray):
- # feed w_array back into array() for other properties
- return array(space, w_array, w_dtype, False, w_order, subok, ndmin)
- else:
- raise oefmt(space.w_ValueError,
- "object __array__ method not producing an array")
+ w_array = try_array_method(space, w_object, w_dtype)
+ if w_array is not None:
+ # continue with w_array, but do further operations in place
+ w_object = w_array
+ copy = False
dtype = descriptor.decode_w_dtype(space, w_dtype)
@@ -57,19 +78,10 @@
# arrays with correct dtype
if isinstance(w_object, W_NDimArray) and \
(space.is_none(w_dtype) or w_object.get_dtype() is dtype):
- shape = w_object.get_shape()
if copy:
- w_ret = w_object.descr_copy(space)
+ return w_object.descr_copy(space)
else:
- if ndmin <= len(shape):
- return w_object
- new_impl = w_object.implementation.set_shape(space, w_object, shape)
- w_ret = W_NDimArray(new_impl)
- if ndmin > len(shape):
- shape = [1] * (ndmin - len(shape)) + shape
- w_ret.implementation = w_ret.implementation.set_shape(space,
- w_ret, shape)
- return w_ret
+ return w_object
# not an array or incorrect dtype
shape, elems_w = strides.find_shape_and_elems(space, w_object, dtype)
@@ -81,8 +93,6 @@
# promote S0 -> S1, U0 -> U1
dtype = descriptor.variable_dtype(space, dtype.char + '1')
- if ndmin > len(shape):
- shape = [1] * (ndmin - len(shape)) + shape
w_arr = W_NDimArray.from_shape(space, shape, dtype, order=order)
if len(elems_w) == 1:
w_arr.set_scalar_value(dtype.coerce(space, elems_w[0]))
@@ -91,6 +101,33 @@
return w_arr
+def numpify(space, w_object):
+ """Convert the object to a W_NumpyObject"""
+ # XXX: code duplication with _array()
+ from pypy.module.micronumpy import strides
+ if isinstance(w_object, W_NumpyObject):
+ return w_object
+ # for anything that isn't already an array, try __array__ method first
+ w_array = try_array_method(space, w_object)
+ if w_array is not None:
+ return w_array
+
+ shape, elems_w = strides.find_shape_and_elems(space, w_object, None)
+ dtype = strides.find_dtype_for_seq(space, elems_w, None)
+ if dtype is None:
+ dtype = descriptor.get_dtype_cache(space).w_float64dtype
+ elif dtype.is_str_or_unicode() and dtype.elsize < 1:
+ # promote S0 -> S1, U0 -> U1
+ dtype = descriptor.variable_dtype(space, dtype.char + '1')
+
+ if len(elems_w) == 1:
+ return dtype.coerce(space, elems_w[0])
+ else:
+ w_arr = W_NDimArray.from_shape(space, shape, dtype)
+ loop.assign(space, w_arr, elems_w)
+ return w_arr
+
+
def _zeros_or_empty(space, w_shape, w_dtype, w_order, zero):
dtype = space.interp_w(descriptor.W_Dtype,
space.call_function(space.gettypefor(descriptor.W_Dtype), w_dtype))
diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py
--- a/pypy/module/micronumpy/ufuncs.py
+++ b/pypy/module/micronumpy/ufuncs.py
@@ -7,6 +7,7 @@
from rpython.tool.sourcetools import func_with_new_name
from pypy.module.micronumpy import boxes, descriptor, loop, constants as NPY
from pypy.module.micronumpy.base import convert_to_array, W_NDimArray
+from pypy.module.micronumpy.ctors import numpify
from pypy.module.micronumpy.strides import shape_agreement
@@ -17,6 +18,13 @@
def done_if_false(dtype, val):
return not dtype.itemtype.bool(val)
+def _get_dtype(space, w_npyobj):
+ if isinstance(w_npyobj, boxes.W_GenericBox):
+ return w_npyobj.get_dtype(space)
+ else:
+ assert isinstance(w_npyobj, W_NDimArray)
+ return w_npyobj.get_dtype()
+
class W_Ufunc(W_Root):
_immutable_fields_ = [
@@ -304,8 +312,8 @@
out = args_w[1]
if space.is_w(out, space.w_None):
out = None
- w_obj = convert_to_array(space, w_obj)
- dtype = w_obj.get_dtype()
+ w_obj = numpify(space, w_obj)
+ dtype = _get_dtype(space, w_obj)
if dtype.is_flexible():
raise OperationError(space.w_TypeError,
space.wrap('Not implemented for this type'))
@@ -315,7 +323,7 @@
raise oefmt(space.w_TypeError,
"ufunc %s not supported for the input type", self.name)
calc_dtype = find_unaryop_result_dtype(space,
- w_obj.get_dtype(),
+ dtype,
promote_to_float=self.promote_to_float,
promote_bools=self.promote_bools)
if out is not None:
@@ -345,6 +353,7 @@
else:
out.fill(space, w_val)
return out
+ assert isinstance(w_obj, W_NDimArray)
shape = shape_agreement(space, w_obj.get_shape(), out,
broadcast_down=False)
return loop.call1(space, shape, self.func, calc_dtype, res_dtype,
@@ -385,10 +394,10 @@
else:
[w_lhs, w_rhs] = args_w
w_out = None
- w_lhs = convert_to_array(space, w_lhs)
- w_rhs = convert_to_array(space, w_rhs)
- w_ldtype = w_lhs.get_dtype()
- w_rdtype = w_rhs.get_dtype()
+ w_lhs = numpify(space, w_lhs)
+ w_rhs = numpify(space, w_rhs)
+ w_ldtype = _get_dtype(space, w_lhs)
+ w_rdtype = _get_dtype(space, w_rhs)
if w_ldtype.is_str() and w_rdtype.is_str() and \
self.comparison_func:
pass
@@ -451,6 +460,12 @@
else:
out = arr
return out
+ if isinstance(w_lhs, boxes.W_GenericBox):
+ w_lhs = W_NDimArray.from_scalar(space, w_lhs)
+ assert isinstance(w_lhs, W_NDimArray)
+ if isinstance(w_rhs, boxes.W_GenericBox):
+ w_rhs = W_NDimArray.from_scalar(space, w_rhs)
+ assert isinstance(w_rhs, W_NDimArray)
new_shape = shape_agreement(space, w_lhs.get_shape(), w_rhs)
new_shape = shape_agreement(space, new_shape, out, broadcast_down=False)
return loop.call2(space, new_shape, self.func, calc_dtype,
diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
--- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
@@ -30,6 +30,7 @@
""")
def test_array_getitem_accumulate(self):
+ """Check that operations/ufuncs on array items are jitted correctly"""
def main():
import _numpypy.multiarray as np
arr = np.zeros((300, 300))
@@ -43,7 +44,6 @@
log = self.run(main, [])
assert log.result == 0
loop, = log.loops_by_filename(self.filepath)
- skip('used to pass on 69421-f3e717c94913')
assert loop.match("""
i81 = int_lt(i76, 300)
guard_true(i81, descr=...)
More information about the pypy-commit
mailing list