Repository: numba/numba

Issue:
const_array is absurdly slow
Consider the following:

``` python
import numba
import numpy as np
x = np.random.rand(100,100,100,100)
@numba.jit(nopython = True, locals={'x':'f8[:,:,:,:]'})
def nose(y):
   y += x[0,0,0,0]
nose(1)
```

Even declaring explicitly the type,  numba seems to python-cycle though every element in the array (the `get_constant` function).
The equivalent:

``` python
@numba.jit(nopython = True)
def nose2(x,y):
   y += x[0,0,0,0]
```

works fine.



Discussion:
I agree, it is very slow for the reason that you outline.

I'm labelling this issue as a bug, since it makes it difficult to use large constant arrays.

Confirmed on 0.23.0.


Resolving patch:
diff --git a/numba/targets/base.py b/numba/targets/base.py
--- a/numba/targets/base.py
+++ b/numba/targets/base.py
@@ -844,20 +844,16 @@ def make_tuple(self, builder, typ, values):
         return tup
 
     def make_constant_array(self, builder, typ, ary):
+        """
+        Create an array structure reifying the given constant array.
+        A low-level contiguous array constant is created in the LLVM IR.
+        """
         assert typ.layout == 'C'                # assumed in typeinfer.py
-        ary = numpy.ascontiguousarray(ary)
-        flat = ary.flatten()
 
-        # Handle data
-        if self.is_struct_type(typ.dtype):
-            values = [self.get_constant_struct(builder, typ.dtype, flat[i])
-                      for i in range(flat.size)]
-        else:
-            values = [self.get_constant(typ.dtype, flat[i])
-                      for i in range(flat.size)]
-
-        lldtype = values[0].type
-        consts = Constant.array(lldtype, values)
+        # Handle data: reify the flattened array in "C" order as a
+        # global array of bytes.
+        flat = ary.flatten()
+        consts = Constant.array(Type.int(8), bytearray(flat))
         data = cgutils.global_constant(builder, ".const.array.data", consts)
 
         # Handle shape
@@ -865,9 +861,9 @@ def make_constant_array(self, builder, typ, ary):
         shapevals = [self.get_constant(types.intp, s) for s in ary.shape]
         cshape = Constant.array(llintp, shapevals)
 
-
-        # Handle strides
-        stridevals = [self.get_constant(types.intp, s) for s in ary.strides]
+        # Handle strides: use strides of the equivalent C-contiguous array.
+        contig = numpy.ascontiguousarray(ary)
+        stridevals = [self.get_constant(types.intp, s) for s in contig.strides]
         cstrides = Constant.array(llintp, stridevals)
 
         # Create array structure


Test patch:
diff --git a/numba/tests/test_arrayconst.py b/numba/tests/test_arrayconst.py
--- a/numba/tests/test_arrayconst.py
+++ b/numba/tests/test_arrayconst.py
@@ -7,16 +7,37 @@
 from numba.errors import TypingError
 from numba import types
 
-myarray = np.arange(5)
-myscalar = np.int32(64)
 
+s1 = np.int32(64)
 
-def use_array_const(i):
-    return myarray[i]
+a1 = np.arange(12)
+a2 = a1[::2]
+a3 = a1.reshape((3, 4)).T
+
+dt = np.dtype([('x', np.int8), ('y', 'S3')])
+
+a4 = np.arange(32, dtype=np.int8).view(dt)
+a5 = a4[::-2]
+
+
+def getitem1(i):
+    return a1[i]
+
+def getitem2(i):
+    return a2[i]
+
+def getitem3(i):
+    return a3[i]
+
+def getitem4(i):
+    return a4[i]
+
+def getitem5(i):
+    return a5[i]
 
 
 def use_arrayscalar_const():
-    return myscalar
+    return s1
 
 
 def write_to_global_array():
@@ -24,12 +45,27 @@ def write_to_global_array():
 
 
 class TestConstantArray(unittest.TestCase):
-    def test_array_const(self):
-        pyfunc = use_array_const
+
+    def check_array_const(self, pyfunc):
         cres = compile_isolated(pyfunc, (types.int32,))
         cfunc = cres.entry_point
         for i in [0, 1, 2]:
-            self.assertEqual(pyfunc(i), cfunc(i))
+            np.testing.assert_array_equal(pyfunc(i), cfunc(i))
+
+    def test_array_const_1d_contig(self):
+        self.check_array_const(getitem1)
+
+    def test_array_const_1d_noncontig(self):
+        self.check_array_const(getitem2)
+
+    def test_array_const_2d(self):
+        self.check_array_const(getitem3)
+
+    def test_record_array_const_contig(self):
+        self.check_array_const(getitem4)
+
+    def test_record_array_const_noncontig(self):
+        self.check_array_const(getitem5)
 
     def test_arrayscalar_const(self):
         pyfunc = use_arrayscalar_const
@@ -46,4 +82,3 @@ def test_write_to_global_array(self):
 
 if __name__ == '__main__':
     unittest.main()
-