Author: Ronan Lamy <[email protected]>
Branch:
Changeset: r79997:d7a28e98dab5
Date: 2015-10-06 01:16 +0100
http://bitbucket.org/pypy/pypy/changeset/d7a28e98dab5/
Log: Merged in issue-2148 (pull request #336)
Fix performance regression on operations mixing numpy scalars and
Python floats.
Closes issue #2148
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
@@ -3,10 +3,13 @@
from rpython.rlib.buffer import SubBuffer
from rpython.rlib.rstring import strip_spaces
from rpython.rtyper.lltypesystem import lltype, rffi
+
from pypy.module.micronumpy import descriptor, loop, support
from pypy.module.micronumpy.base import (
W_NDimArray, convert_to_array, W_NumpyObject)
from pypy.module.micronumpy.converters import shape_converter
+from . import constants as NPY
+from .casting import scalar2dtype
def build_scalar(space, w_dtype, w_state):
@@ -82,7 +85,6 @@
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):
@@ -143,16 +145,11 @@
w_base=w_base, start=imp.start)
else:
# not an array
- shape, elems_w = strides.find_shape_and_elems(space, w_object, dtype)
+ shape, elems_w = find_shape_and_elems(space, w_object, dtype)
if dtype is None and space.isinstance_w(w_object, space.w_buffer):
dtype = descriptor.get_dtype_cache(space).w_uint8dtype
if dtype is None or (dtype.is_str_or_unicode() and dtype.elsize < 1):
dtype = find_dtype_for_seq(space, elems_w, dtype)
- 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')
w_arr = W_NDimArray.from_shape(space, shape, dtype, order=order)
if support.product(shape) == 1: # safe from overflow since from_shape
checks
@@ -165,7 +162,6 @@
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
@@ -173,20 +169,82 @@
if w_array is not None:
return w_array
- shape, elems_w = strides.find_shape_and_elems(space, w_object, None)
+ if is_scalar_like(space, w_object, dtype=None):
+ dtype = scalar2dtype(space, w_object)
+ if dtype.is_str_or_unicode() and dtype.elsize < 1:
+ # promote S0 -> S1, U0 -> U1
+ dtype = descriptor.variable_dtype(space, dtype.char + '1')
+ return dtype.coerce(space, w_object)
+
+ shape, elems_w = _find_shape_and_elems(space, w_object)
dtype = 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')
+ w_arr = W_NDimArray.from_shape(space, shape, dtype)
+ loop.assign(space, w_arr, elems_w)
+ return w_arr
- if len(elems_w) == 1:
- return dtype.coerce(space, elems_w[0])
+
+def find_shape_and_elems(space, w_iterable, dtype):
+ if is_scalar_like(space, w_iterable, dtype):
+ return [], [w_iterable]
+ is_rec_type = dtype is not None and dtype.is_record()
+ return _find_shape_and_elems(space, w_iterable, is_rec_type)
+
+def is_scalar_like(space, w_obj, dtype):
+ isstr = space.isinstance_w(w_obj, space.w_str)
+ if not support.issequence_w(space, w_obj) or isstr:
+ if dtype is None or dtype.char != NPY.CHARLTR:
+ return True
+ is_rec_type = dtype is not None and dtype.is_record()
+ if is_rec_type and is_single_elem(space, w_obj, is_rec_type):
+ return True
+ if isinstance(w_obj, W_NDimArray) and w_obj.is_scalar():
+ return True
+ return False
+
+def _find_shape_and_elems(space, w_iterable, is_rec_type=False):
+ from pypy.objspace.std.bufferobject import W_Buffer
+ shape = [space.len_w(w_iterable)]
+ if space.isinstance_w(w_iterable, space.w_buffer):
+ batch = [space.wrap(0)] * shape[0]
+ for i in range(shape[0]):
+ batch[i] = space.ord(space.getitem(w_iterable, space.wrap(i)))
else:
- w_arr = W_NDimArray.from_shape(space, shape, dtype)
- loop.assign(space, w_arr, elems_w)
- return w_arr
+ batch = space.listview(w_iterable)
+ while True:
+ if not batch:
+ return shape[:], []
+ if is_single_elem(space, batch[0], is_rec_type):
+ for w_elem in batch:
+ if not is_single_elem(space, w_elem, is_rec_type):
+ raise OperationError(space.w_ValueError, space.wrap(
+ "setting an array element with a sequence"))
+ return shape[:], batch
+ new_batch = []
+ size = space.len_w(batch[0])
+ for w_elem in batch:
+ if (is_single_elem(space, w_elem, is_rec_type) or
+ space.len_w(w_elem) != size):
+ raise OperationError(space.w_ValueError, space.wrap(
+ "setting an array element with a sequence"))
+ w_array = space.lookup(w_elem, '__array__')
+ if w_array is not None:
+ # Make sure we call the array implementation of listview,
+ # since for some ndarray subclasses (matrix, for instance)
+ # listview does not reduce but rather returns the same class
+ w_elem = space.get_and_call_function(w_array, w_elem,
space.w_None)
+ new_batch += space.listview(w_elem)
+ shape.append(size)
+ batch = new_batch
+
+def is_single_elem(space, w_elem, is_rec_type):
+ if (is_rec_type and space.isinstance_w(w_elem, space.w_tuple)):
+ return True
+ if (space.isinstance_w(w_elem, space.w_tuple) or
+ space.isinstance_w(w_elem, space.w_list)):
+ return False
+ if isinstance(w_elem, W_NDimArray) and not w_elem.is_scalar():
+ return False
+ return True
def _dtype_guess(space, dtype, w_elem):
from .casting import scalar2dtype, find_binop_result_dtype
@@ -201,6 +259,11 @@
return _dtype_guess(space, dtype, w_elem)
for w_elem in elems_w:
dtype = _dtype_guess(space, dtype, w_elem)
+ 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')
return dtype
diff --git a/pypy/module/micronumpy/strides.py
b/pypy/module/micronumpy/strides.py
--- a/pypy/module/micronumpy/strides.py
+++ b/pypy/module/micronumpy/strides.py
@@ -189,67 +189,6 @@
return rstrides, rbackstrides
-def is_single_elem(space, w_elem, is_rec_type):
- if (is_rec_type and space.isinstance_w(w_elem, space.w_tuple)):
- return True
- if (space.isinstance_w(w_elem, space.w_tuple) or
- space.isinstance_w(w_elem, space.w_list)):
- return False
- if isinstance(w_elem, W_NDimArray) and not w_elem.is_scalar():
- return False
- return True
-
-
-def find_shape_and_elems(space, w_iterable, dtype):
- isstr = space.isinstance_w(w_iterable, space.w_str)
- if not support.issequence_w(space, w_iterable) or isstr:
- if dtype is None or dtype.char != NPY.CHARLTR:
- return [], [w_iterable]
- is_rec_type = dtype is not None and dtype.is_record()
- if is_rec_type and is_single_elem(space, w_iterable, is_rec_type):
- return [], [w_iterable]
- if isinstance(w_iterable, W_NDimArray) and w_iterable.is_scalar():
- return [], [w_iterable]
- return _find_shape_and_elems(space, w_iterable, is_rec_type)
-
-
-def _find_shape_and_elems(space, w_iterable, is_rec_type):
- from pypy.objspace.std.bufferobject import W_Buffer
- shape = [space.len_w(w_iterable)]
- if space.isinstance_w(w_iterable, space.w_buffer):
- batch = [space.wrap(0)] * shape[0]
- for i in range(shape[0]):
- batch[i] = space.ord(space.getitem(w_iterable, space.wrap(i)))
- else:
- batch = space.listview(w_iterable)
- while True:
- if not batch:
- return shape[:], []
- if is_single_elem(space, batch[0], is_rec_type):
- for w_elem in batch:
- if not is_single_elem(space, w_elem, is_rec_type):
- raise OperationError(space.w_ValueError, space.wrap(
- "setting an array element with a sequence"))
- return shape[:], batch
- new_batch = []
- size = space.len_w(batch[0])
- for w_elem in batch:
- if (is_single_elem(space, w_elem, is_rec_type) or
- space.len_w(w_elem) != size):
- raise OperationError(space.w_ValueError, space.wrap(
- "setting an array element with a sequence"))
- w_array = space.lookup(w_elem, '__array__')
- if w_array is not None:
- # Make sure we call the array implementation of listview,
- # since for some ndarray subclasses (matrix, for instance)
- # listview does not reduce but rather returns the same class
- w_elem = space.get_and_call_function(w_array, w_elem,
space.w_None)
- new_batch += space.listview(w_elem)
- shape.append(size)
- batch = new_batch
-
-
-
@jit.unroll_safe
def shape_agreement(space, shape1, w_arr2, broadcast_down=True):
if w_arr2 is None:
diff --git a/pypy/module/micronumpy/test/test_ndarray.py
b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -169,7 +169,7 @@
[1, 1, 1, 105, 105]
def test_find_shape(self):
- from pypy.module.micronumpy.strides import find_shape_and_elems
+ from pypy.module.micronumpy.ctors import find_shape_and_elems
space = self.space
shape, elems = find_shape_and_elems(space,
@@ -2485,7 +2485,7 @@
x = array([(u'a', 'b')], dtype=t)
x['a'] = u'1'
assert str(x) == "[(u'1', 'b')]"
-
+
def test_boolean_indexing(self):
import numpy as np
@@ -2709,7 +2709,7 @@
"input array from shape (3,1) into shape (3)"
a[:, 1] = b[:,0] > 0.5
assert (a == [[0, 1], [0, 1], [0, 1]]).all()
-
+
def test_ufunc(self):
from numpy import array
@@ -3868,7 +3868,7 @@
assert a[0]['y'] == 2
assert a[1]['y'] == 1
-
+
a = array([(1, [])], dtype=[('a', int32), ('b', int32, 0)])
assert a['b'].shape == (1, 0)
b = loads(dumps(a))
diff --git a/pypy/module/micronumpy/test/test_scalar.py
b/pypy/module/micronumpy/test/test_scalar.py
--- a/pypy/module/micronumpy/test/test_scalar.py
+++ b/pypy/module/micronumpy/test/test_scalar.py
@@ -480,3 +480,9 @@
u = unicode_(u'Aÿ')
# raises(UnicodeEncodeError, "str(u)") # XXX
assert repr(u) == repr(u'Aÿ')
+
+ def test_binop_with_sequence(self):
+ import numpy as np
+ c = np.float64(1.) + [1.]
+ assert isinstance(c, np.ndarray)
+ assert (c == [2.]).all()
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
@@ -479,6 +479,7 @@
dt_in, dt_out = self._calc_dtype(space, dtype, out, casting)
return dt_in, dt_out, self.func
+ @jit.unroll_safe
def _calc_dtype(self, space, arg_dtype, out=None, casting='unsafe'):
if arg_dtype.is_object():
return arg_dtype, arg_dtype
@@ -672,6 +673,7 @@
"requested type has type code '%s'" % (self.name, dtype.char))
+ @jit.unroll_safe
def _calc_dtype(self, space, l_dtype, r_dtype, out, casting,
w_arg1, w_arg2):
if l_dtype.is_object() or r_dtype.is_object():
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
@@ -248,3 +248,42 @@
guard_false(i157, descr=...)
jump(..., descr=...)
""")
+
+ def test_mixed_div(self):
+ N = 1500
+ def main():
+ N = 1500
+ import _numpypy.multiarray as np
+ arr = np.zeros(N)
+ l = [arr[i]/2. for i in range(N)]
+ return l
+ log = self.run(main, [])
+ assert log.result == [0.] * N
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match("""
+ i92 = int_ge(i91, i37)
+ guard_false(i92, descr=...)
+ i93 = int_add(i91, 1)
+ setfield_gc(p23, i93, descr=<FieldS
pypy.objspace.std.iterobject.W_AbstractSeqIterObject.inst_index 8>)
+ i94 = int_ge(i91, i56)
+ guard_false(i94, descr=...)
+ i96 = int_mul(i91, i58)
+ i97 = int_add(i51, i96)
+ f98 = raw_load_f(i63, i97, descr=<ArrayF 8>)
+ guard_not_invalidated(descr=...)
+ f100 = float_mul(f98, 0.500000)
+ i101 = int_add(i79, 1)
+ i102 = arraylen_gc(p85, descr=<ArrayP 8>)
+ i103 = int_lt(i102, i101)
+ cond_call(i103,
ConstClass(_ll_list_resize_hint_really_look_inside_iff__listPtr_Signed_Bool),
p76, i101, 1, descr=<Callv 0 rii EF=5>)
+ guard_no_exception(descr=...)
+ p104 = getfield_gc_r(p76, descr=<FieldP list.items 16>)
+ p105 = new_with_vtable(descr=<SizeDescr 24>)
+ setfield_gc(p105, f100, descr=<FieldF
pypy.module.micronumpy.boxes.W_Float64Box.inst_value 16>)
+ setarrayitem_gc(p104, i79, p105, descr=<ArrayP 8>)
+ i106 = getfield_raw_i(#, descr=<FieldS pypysig_long_struct.c_value
0>)
+ setfield_gc(p76, i101, descr=<FieldS list.length 8>)
+ i107 = int_lt(i106, 0)
+ guard_false(i107, descr=...)
+ jump(..., descr=...)
+ """)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit