numpy/numpy — pull request #3421 by juliantaylor (2013-06-10 17:49:02+00:00)
Title: Poor performance complex strided copies since 1.7

complex strided copies are very slow compared to non complex types since numpy 1.7:
on an amd x4:
```
In [2]: d = np.arange(200*1000, dtype=np.float64).reshape((1000,200))
In [3]: e = np.arange(200*1000, dtype=np.float64).reshape((200,1000))
In [4]: %timeit d[...] = e.T
1000 loops, best of 3: 510 µs per loop

In [5]: d = np.arange(100*1000, dtype=np.complex128).reshape((1000,100))
In [6]: e = np.arange(100*1000, dtype=np.complex128).reshape((100,1000))
In [7]: %timeit d[...] = e.T
1000 loops, best of 3: 1.47 ms per loop
```
on an intel I even saw a tenfold slowdown.

The reason for this is twofold, for one the fix for gh-2668 set all complex types to unaligned.
This causes lowlevel_strided_loops to use elementwise memmoves which is extremely slow.
This would be somewhat fine if it were limited to architectures which can't load unaligned data, but even on x86 it uses the unaligned loads for a pretty bogus reason.

So to fix this I for one enabled unaligned loads for x86 and more invasive removed the unaligned setting from complex arrays.
Instead I fixed the alignment requirement to actually be correct, hopefully that fixes the sparc segfault but I can't verify that yet.

with the fix:

```
In [5]: d = np.arange(100*1000, dtype=np.complex128).reshape((1000,100))
In [6]: e = np.arange(100*1000, dtype=np.complex128).reshape((100,1000))

In [7]: %timeit d[...] = e.T
1000 loops, best of 3: 397 µs per loop
```
(which is faster than float64 due to the unrolling in this case, I'll post a PR which improves non float type to the same speed later.)

testing this is actually a little tricky, as i386 aligns doubles to 4 byte by default so complex is 8 byte. This is fine as it can do unaligned loads.
One could align to 16 byte also on i386 and make sure the flags are properly set in ctors.c (that introduces whole a bunch of test failures as almost all array flags change on i386)