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&#255;')
         # raises(UnicodeEncodeError, "str(u)")  # XXX
         assert repr(u) == repr(u'A&#255;')
+
+    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

Reply via email to