Repository: mars-project/mars

Issue:
`LU` decomposition is super slow when input has one chunk
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

Sample code: 

```
In[1]: import mars.tensor as mt
In[2]: import numpy as np
In[3]: import scipy.linalg as SA

In[4]: a = np.random.rand(1000, 3)
In[5]: %time SA.lu(a)
CPU times: user 2.67 ms, sys: 2.2 ms, total: 4.87 ms
Wall time: 4.85 ms
Out[5]: 
(array([[0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 1., 0., 0.],
        [0., 0., 0., ..., 0., 1., 0.],
        [0., 0., 0., ..., 0., 0., 1.]]),
 array([[ 1.        ,  0.        ,  0.        ],
        [ 0.00792837,  1.        ,  0.        ],
        [ 0.82631769, -0.40739556,  1.        ],
        ...,
        [ 0.73515962, -0.36569706,  0.9168457 ],
        [ 0.97522005, -0.33465358,  0.32019001],
        [ 0.12172773,  0.77446919, -0.01518423]]),
 array([[0.99918237, 0.4830823 , 0.19349809],
        [0.        , 0.96498435, 0.66580513],
        [0.        , 0.        , 1.0709762 ]]))

In[6]: %time mt.linalg.lu(mt.array(a)).execute()
CPU times: user 2.36 s, sys: 77.1 ms, total: 2.43 s
Wall time: 1.9 s
Out[6]: 
[array([[0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 1., 0., 0.],
        [0., 0., 0., ..., 0., 1., 0.],
        [0., 0., 0., ..., 0., 0., 1.]]),
 array([[ 1.        ,  0.        ,  0.        ],
        [ 0.00792837,  1.        ,  0.        ],
        [ 0.82631769, -0.40739556,  1.        ],
        ...,
        [ 0.73515962, -0.36569706,  0.9168457 ],
        [ 0.97522005, -0.33465358,  0.32019001],
        [ 0.12172773,  0.77446919, -0.01518423]]),
 array([[0.99918237, 0.4830823 , 0.19349809],
        [0.        , 0.96498435, 0.66580513],
        [0.        , 0.        , 1.0709762 ]])]

```

And due to the slow speed of `lu`, `randomized_svd` performs quite slow either which causes that CI took too much time. (Main cost is the `PCA` and `TruncatedSVD` cases that leverages `randomized_svd`.


Discussion:

Resolving patch:
diff --git a/mars/tensor/linalg/lu.py b/mars/tensor/linalg/lu.py
--- a/mars/tensor/linalg/lu.py
+++ b/mars/tensor/linalg/lu.py
@@ -73,8 +73,35 @@ def __call__(self, a):
                                    ])
         return ExecutableTuple([p, l, u])
 
+    @classmethod
+    def _tile_one_chunk(cls, op):
+        p, l, u = op.outputs
+        chunk_op = op.copy().reset_key()
+        chunk_kws = [
+            {'side': 'p', 'dtype': p.dtype,
+             'shape': p.shape, 'order': p.order,
+             'index': (0,) * p.ndim},
+            {'side': 'l', 'dtype': l.dtype,
+             'shape': l.shape, 'order': l.order,
+             'index': (0,) * l.ndim},
+            {'side': 'u', 'dtype': u.dtype,
+             'shape': u.shape, 'order': u.order,
+             'index': (0,) * u.ndim}
+        ]
+        chunks = chunk_op.new_chunks(op.input.chunks, kws=chunk_kws)
+
+        new_op = op.copy()
+        kws = [p.params, l.params, u.params]
+        for i, out in enumerate([p, l, u]):
+            kws[i]['nsplits'] = tuple((s,) for s in out.shape)
+            kws[i]['chunks'] = [chunks[i]]
+        return new_op.new_tensors(op.inputs, kws=kws)
+
     @classmethod
     def tile(cls, op):
+        if len(op.input.chunks) == 1:
+            return cls._tile_one_chunk(op)
+
         from ..arithmetic.subtract import TensorSubtract
         from ..arithmetic.add import TensorTreeAdd
         from ..base.transpose import TensorTranspose


Test patch: