4 ms·
Just another data point. I've just tested pythran (a numeric python to C++ compiler) with the code from https://discourse.julialang.org/t/i-just-decided-to-migr
by cycomanic 5y ago
Just another data point. I've just tested pythran (a numeric python to C++ compiler) with the code from https://discourse.julialang.org/t/i-just-decided-to-migrate-from-python-fortran-to-julia-as-julia-was-faster-in-my-test/61188/12 https://discourse.julialang.org/t/i-just-decided-to-migrate-...
On my machine the julia code on my PC is:
N = 2000
88.273 ms (8 allocations: 61.04 MiB)
49.445 ms (3 allocations: 61.05 MiB)
14.599 ms (2 allocations: 61.04 MiB)
If I compile the following code with pythran:
#pythran export texp2(int)
def texp2(N):
a = np.linspace(0,2*np.pi,N)
return np.exp(1j*100*(a[:,np.newaxis]**2+a[np.newaxis,:]**2))
I get:
timeit texp2(2000)
12.3 ms ± 27.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
So actually slightly faster than the optimized julia code.
- eigenspace 5y agoHow does Pythran fare against the code in this link: https://discourse.julialang.org/t/i-just-decided-to-migrate-from-python-fortran-to-julia-as-julia-was-faster-in-my-test/61188/9 https://discourse.julialang.org/t/i-just-decided-to-migrate-... The author made some julia performance mistakes in his benchmarks. The code in this version: https://discourse.julialang.org/t/i-just-decided-to-migrate-from-python-fortran-to-julia-as-julia-was-faster-in-my-test/61188/12 https://discourse.julialang.org/t/i-just-decided-to-migrate-... should be very tricky to beat, and I suspect that the only way is doing a lot of customized assembly.
- cycomanic 5y agoThat is the code I'm comparing against. So the fastest Julia version is actually the one using @tturbo in your link. I'm not surprised, pythran gets me the best performance across numba, customized cython and (for some comparisons) julia in most of my tests.
- eigenspace 5y agoAh very interesting, this is great to know! Sorry I didn't properly read your initial comment. I'm now quite interested to see what we can do to match or beat Pythran here. What CPU are you using? I know my CPU, a Zen+ Ryzen 5 2600, is one of the worst modern CPUs for this sort of thing and there's much better scaling on Intel CPUs or the Zen 3 CPUs, but those things are probably also effecting the Pythran code as well. Is Pythran any good at producing BLAS microkernels?
- celrod 5y agoWould be good to confirm the number of threads being used by both, but it could also be that pythran is using a better `sincos` implementation. On very old hardware (which it would have to be if multithreaded), the exp implementation might not be great either. That'd especially be the case if it has no AVX, as the exp implementation relies on a table (which requires gather instructions). For 2000x2000, their time was 14ms, vs 6ms for the 2600.
- eigenspace 5y agoYeah, I tried to get Pythran working on my computer to compare against, but it wouldn't build for me.
- cycomanic 5y agoJust an additional comment, using sincos in the comparison is not really fair, because it is a change to the algorithm (it assumes that a is real), because in general exp(x) = exp(x.real())+ sincos(x.imag()) (for simplification sincos(x) returns cos(x) + jsin(x), but you get the point). The other code can't easily optimise for that (although it would be a nice optimisation). Interestingly (and I can't explain why, maybe someone knowing more about julia), if I change the code of that third method using tturbo to use the cis function (or exp) it becomes a factor of 20 slower and is actually the slowest of all the different algorithms)
- celrod 5y agoexp(x) = exp(x.real()) * sincos(x.imag()) Anyway, it is the same algorithm as long as `x` is real. The reason for manually inlining exp(::Complex) and manually decomposing the complex number into its real and imaginary parts is that `@tturbo` currently only supports real inputs. Given types that it does not support, it will silently run the original loop single-threaded and optimized simply with `@inbounds @fastmath`. I imagine in changing to cis or exp, you also switched to using complex numbers? If so, that would explain the dramatic slowdown. If not, mind sharing your example, here or in an issue at LoopVectorization.jl? It's planned that "some day" LoopVectorization will support complex numbers through the AbstractInterpreter interface, but this is probably still a ways off.
- celrod 5y agoDid you start Julia with multiple threads? Julia unfortunately starts with only a single thread by default, and the slow time you reported (14 ms) makes it appear that this may have been the case. You can start `julia -t4` for 4 threads, for example. Or you can set the environmental variable `JULIA_NUM_THREADS=4`. If you were using the same number of threads for both, mind letting me know which CPU you're using?
- cycomanic 5y agoYou were correct, I just tested with julia -t4 and that improved times by about a factor of 3. N = 2000 27.137 ms (24 allocations: 61.04 MiB) 49.496 ms (3 allocations: 61.05 MiB) 4.597 ms (2 allocations: 61.04 MiB) The CPU is a ryzen 3600 btw.
- celrod 5y ago-t6 would give 6 threads. FWIW, the Ryzen 2600 with 6 threads took 6 ms, so the 3600's floating point and vector improvement is quite evident from your timing. (The 2600 emulates 256-bit operations with 2x 128-bit, while the 3600 has full support.)
- cycomanic 5y agoYes, I've also run with 6 threads and get an additional minor improvement on the loopvectorized code (3.9 vs 4.5ms). I have to say LoopVectorization.jl is quite an impressive piece of work. I have to try out some more julia at some point, unfortunately pretty much everything I do is using complex values.
- UncleOxidant 5y agoCan you post the C++ that was generated somewhere?
- cycomanic 5y agoIt's heavy templated code, which I don't think is very useful without the pythran source, but here it is: https://gist.github.com/cycomanic/a0b16ab0c45b3d888a818d17a021b27b https://gist.github.com/cycomanic/a0b16ab0c45b3d888a818d17a0...