22 ms·
Julia: Faster than Fortran, cleaner than Numpy
- elcomet 5y agoAnd slower (to start) than both of them. I tried julia to write some scripts that were previously in numpy, but the slow start threw me off. I hope they solve this because it's a great language.
- davrosthedalek 5y agoI thought Julia is a compiled language. Surprising that the startup is then so slow.
- najtdale 5y agoJIT compiled. Has a warmup time.
- davrosthedalek 5y agoAh, I see. That explains a lot. Could also be the reason why it beats gfortran in the test. A JIT knows more about the data the program sees/runtime behavior than a normal compiler. Still surprising. Or is gfortran just so much behind state of the art fortran compilers?
- eigenspace 5y agoNot this test. Julia’s JIT compiler does not know about runtime values, it’s not a tracing JIT like the ones you may be used to. Instead, it’s ‘just ahead of time’, compiling a specialized executable for each new function signature the first time that signature gets hit. I.e. if I do f(x) = 2x + 1 The first time I call f(1) it’ll compile a function specialization for f( ::Int), and the first time you call f on that integer it’ll be slow and all the subsequent calls will be equally fast. Next if you do f(1.0 + 2im) it’ll compile a new specialization for f(::Complex{Float64}) which will be slow the first time while it compiles and then fast on all the subsequent runs. The genius of Julia’s design is that the JIT compiler is designed around the semantics of multiple dispatch, and the multiple dispatch semantics are designed around having a JIT
- pletnes 5y agoIt’s just-ahead-of-time compiled and no «interpreted mode» so it’s always slow to start, if I understand correctly. Really annoying, especially the first time you use it.
- cbkeller 5y agoIf you need to run small scripts and can't switch to a persistent-REPL-based workflow, you might consider starting Julia with the `--compile=min` option. If you know which packages you're going to need, you can also reduce startup times dramatically by building a sysimg with PackageCompiler.jl There is also technically an interpreter if you want to go that way [1], so in principle it might be possible to do the same trick javascript does, but someone would have to implement that. [1] https://github.com/JuliaDebug/JuliaInterpreter.jl https://github.com/JuliaDebug/JuliaInterpreter.jl
- mwest217 5y agoThat’s strictly accurate but kinda misleading. It doesn’t have multiple optimization passes it does just for hot code paths, it fully compiled per function. They call it Just Ahead Of Time compiled.
- acmj 5y agoThink about compiling a large C++ program and its dependencies every time you run the generated executable. This is sort of like what is Julia is doing.
- eigenspace 5y agoThe startup times have improved a lot in version 1.6, the latest release, and are I Proving again in the next upcoming release candidate. Are you hitting the startup time very often? If so, you might want to try some things to keep one julia session open and sending code to it like a daemon instead of constantly closing and opening sessions. This package makes that workflow really easy: https://github.com/dmolina/DaemonMode.jl https://github.com/dmolina/DaemonMode.jl
- superdimwit 5y agoI most often need to restart the REPL when redefining structs.
- dosshell 5y agoSame for me. Ported my calculations to Python from Julia. Even before Julia has started python was done. And Julia also did jit compilation in runtime. A function call could take seconds on first call.
- moelf 5y agoif it's that fast (<O(1s)) and your use-case is to call it many many times from ground-up, you sure should use Python or even bash since there's literally no performance to speak of. (i.e. nothing really changes the usability anyways)
- jlamberts 5y agoI have also recently been playing around with Julia coming from numpy/pandas. I found that 1.6.1 felt way smoother on startup and import than 1.5.x. Might be worth trying to upgrade if you haven't already. At the very least it's encouraging that it seems like they're making progress in that direction.
- bobbylarrybobby 5y agoUnfortunately Julia is a “repl-based language”. You are expected to open a repl and work in it for as long as possible so that your compilations all remain cached. Packages like Revise can help reevaluate code that changes so that you don’t have to leave the repl. I do wish that Julia would start up in an interpreted mode and compile in the background so that it would be fast enough when first opened and then attain maximum speed later on. (I think this is how JavaScript engines work?)
- eigenspace 5y agoYou can have what you want here by using DaemonMode.jl https://github.com/dmolina/DaemonMode.jl https://github.com/dmolina/DaemonMode.jl
- snicker7 5y agoIt would be nice if their was an "offline version" of this, e.g. automagically cache all compiled code to disk. Daemon mode isn't restart-friendly.
- adgjlsfhk1 5y agoThat's basically what PackageCompiler does. That said, StaticCompiler.jl is expected to improve the state of this in the future.
- snicker7 5y agoNot quite. PackageCompiler isn't automagic. The sysimage doesn't get automatically updated in the REPL. You have to create one manually (and this requires tracing instead of static type inference which Julia can do). So the workflow isn't zero-friction. And StaticCompiler hasn't been updated in over a year. The problem with Julia tooling in general is that they feel 90% done. And tooling is, in my opinion, more important than the language itself.
- 5y ago
- goalieca 5y agoWhenever people run benchmarks on numerical code, especially involving square root, I immediately wonder if the generated output is either ´correct’ or just approximate.
- galangalalgol 5y agoi did notice in the benchmark game they were using fast math with a float and an iteration to get the inverse sqrt. but the c++ and rust examples were doing the same
- kaba0 5y agoThere is no ‘correct’ in floating point, just approximate. But I would guess that such a talented group as Julia’s would pay extreme attention to numerical stability/best implementation of algorithms, or fall back to a lib everyone uses already.
- actinium226 5y agoWow, I may have to give Julia a try. I've been holding back because of that classic language issue "it doesn't have the libraries I like" but these performance numbers are impressive and hard to ignore.
- leephillips 5y agoIn addition to everything else, Julia makes it easy to call functions from Python, C, and Fortran libraries.
- oivey 5y agoThe need for libraries is a lot different, too. In Python when you need a library what you specifically might need is a high performance Python wrapper around a C library. Just writing your own short thing isn’t an option because pure Python is so slow. In Julia that’s not the case.
- cycomanic 5y agoI find the argument that vectorized code is more difficult to read weird. Having to write out the loops becomes tedious very quickly and does often significantly decrease readibility. Take for example the element wise multiplication of two 2d square arrays and compare that to the multiplication of an array and a transpose. In the loop the only difference is that the indices are flipped, easy to overlook in a quick read. My biggest annoyance with Julia is however that they decided to follow matlab and do matrix operations by default and even worse use the '.' as the element wise operation modifier. From my own experience and from teaching many students, one of the primary source of issues is getting that wrong. I don't know how many times when debugging some weird matlab issue it was a matter of find the missing '.' they could have chosen any other symbol but instead the opted for the easiest one to overlook.
- eigenspace 5y agoIMO, doing matrix operations by default is the mathematically correct thing to do. E.g. having exp(M::Matrix) give anything other than the matrix exponential is insanity. Same with A::Matrix * B::Matrix giving anything other than matrix multiplication. As far as the ‘.’ for elementwise operations, I don’t think it’s that hard to miss, but I guess my eyes are trained for it by now. There’s also an @. macro to make an entire expression element wise though if needed, e.g. @. h(f(x) + g(y)) is equivalent to h.(f.(x) .+ g.(y)) I think this broadcasting machinery is one of the closet parts of julia because you get to choose at the call site if you want the function to apply element-wise or to see the whole array, and it works on any container, with user customizable behaviour for user defined containers, and has all sorts of goodies like automatic loop fusion.
- NegativeLatency 5y agoHaving zero experience with this I could see myself getting used to the notation. The dot before the addition sign is strange looking to unfamiliar eyes, is it calling + on the result of f.(x)?
- eigenspace 5y ago
- sdfhbdf 5y agoMaybe a nitpick but why would someone put time on Y axis? I remember it's DRYMIX for graphs - dependent on Y axis (N), independent on X axis (time)
- bramblerose 5y agoBecause what's measured is how how the runtime (on the Y axis) increases as the matrix size N (on the X axis) increases (i.e., you set N and measure t, rather than setting t and measuring N)
- deleted 5y ago[deleted]
- vslira 5y agoI just wish the Julia community wasn't so invested in using UTF8 symbols in the code. I understand that for Physicists, Engineers, Mathematicians, etc it look cleaner and more familiar, but when writing code that is meant to be maintained and read over and over agains it makes a difference if that µ looks too much like a u sometimes. What's so bad about just writing mu? Anyway, this is not a complaint about the language (which I like very much), just a dislike of the popular usage
- moelf 5y agoI'm personally very empathetic to this. Fortunately, I think most "real" developers are pretty restraint regarding this in popular packages' code. Some proper use casees IMHO are: 1. in "terminal" code: scripts, notebooks. 2. function internal variables 3. for making the code look like their counterpart of a paper. idk what to think of 3. if you look at any paper, non of them only uses ASCII, which raises the question, if we're happy with reading papers (even CS ones) with symbols, why not in our code?
- spicybright 5y agoI definitely agree with this. It's just a pain to type out. Maybe I'm behind on this, but needing to use the mouse to find symbols in a huge menu is very painful. You can copy and paste too but again, huge break of flow. Do some editors support discord-style emoji syntax, where typing :fo would bring up a menu of emojis that might match foo. Then hitting enter inserts the emoji over the :foo: representation. You can also not use the auto complete menu. Ex. :pow2: might turn into ²
- agumonkey 5y agoInteresting to see.. for so many years julia was so much in a blur.
- rickard 5y agoI recently (last week) started using numba, for similar reasons to why the author seems to like Julia. I tested translating his example to numba: @numba.njit(parallel=True, fastmath=True) def w(M, a): n = len(a) for i in numba.prange(n): for j in range(n): M[i,j] = np.exp(1j*k * np.sqrt(a[i]**2 + a[j]**2)) and timed it like this: %%timeit n = len(a) M = np.zeros((n,n), dtype=complex) w(M, a) On my 8-core system, this ends up more than 10x as fast as the numpy version he listed (which seems to lack the sqrt, though), which would place it close to the multithreaded Julia, even considering that ran it on a 4-core system. As an added bonus, it can also pretty much automatically translate to GPU as well.
- moelf 5y agothe raising of numba shows us why numpy and "just write vectorize-styled code with a C++ backend" is not enough. yet Numba basically makes your python code not python. It doesn't support so many things: pandas dataframe, or even as simple as a dict(), which means you often have to manually feed your numba function separate arguments. To separate a complicated calculation into numba-infer-able parts and the not ones is not fun and sometimes just impossible.
- rickard 5y agoYep, completely agree. For the project I'm currently doing, it seems like a fairly good fit though. Lots of prototyping different approximations, and needs to be faster than plain numpy. Also, the jitclass things help somewhat. I use them as plain data containers, to work around the hideously long argument lists that otherwise would be required, but with no methods. jitclass breaks the GPU option, though.
- garyrob 5y agoActually, Numba does support dicts now.(You can't have a mix of types in the dicts unless that's changed, but that isn't an actual problem for most ML work.) I have used numba very effectively to make my machine learning research projects run very quickly. I don't use pandas; I do use a lot of numpy and scipy. I understand that pandas can use numpy arrays for at least some things. Since numba works great with numpy, it seems like that might be an approach for using it with pandas, in at least some cases.
- ianhorn 5y agoThis an unfair example of trying to make the numpy readable: n = len(a) M = np.exp(1j*k*(np.tile(a,(n,1))**2 + np.tile(a.reshape(n,1),(1,n))**2)) This has a common issue in functional programming and there's an easy trick to fix it. Change the big one liner by naming things: n = len(a) A = np.tile(a,(n,1)) A_T = np.tile(a.reshape(n,1),(1,n)) M = np.exp(1j*k*(A**2 + A_T**2)) Much easier to read now, and it even exposes an error (the sqrt is missing!). Before we fix that, let's make the obvious simplification of using the .T for transpose: n = len(a) A = np.tile(a,(n,1)) M = np.exp(1j*k*(A**2 + A.T**2)) Then let's use numpy's broadcasting to be more idiomatic: A = a[:, np.newaxis] M = np.exp(1j*k*(A**2 + A.T**2)) Now let's fix the bug: A = a[:, np.newaxis] M = np.exp(1j*k*np.sqrt(A**2 + A.T**2)) or even A = a[:, np.newaxis] root_sum_squares = np.sqrt(A**2 + A.T**2) M = np.exp(1j*k*root_sum_squares) I think the last two of these are fairer comparisons. -------- And to pitch the hype train for jax: if you want to fuse the loops and/or get gpu/tpu for free, just swap np for jnp: A = a[:, jnp.newaxis] M = jnp.exp(1j*k*jnp.sqrt(A**2 + A.T**2))
- moelf 5y ago>if you want to fuse the loops and get gpu/tpu for free the work being done at JAX is great and Julia shares many of the goals (Julia would even be the pioneer in some cases). The best part is your library doesn't even need numpy/JAX as dependency. Yet your function that works on AbstractArray will work on arrays that live on GPU/TPU, for free. thanks to multi-dispatch, you don't need to write a ton of boiler plate code for interface and inherent some classes from JAX/numpy.
- eigenspace 5y agoThe julia implementation was also suboptimal to be fair. I sped it up considerably here: https://discourse.julialang.org/t/i-just-decided-to-migrate-from-python-fortran-to-julia-as-julia-was-faster-in-my-test/61188/6 https://discourse.julialang.org/t/i-just-decided-to-migrate-... and then even further here: 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-... I'd actually be very interested if there's anything other than handwritten assembly that's faster than the second one I posted. ______________ Regarding your more readable version of the Numpy syntax though, I think you have to admit it's still not as readable as for i in 1:N A[i,j] = exp((100+im)*im*sqrt(a[i]^2 + a[j]^2)) end right? I'll also note that the author of the post didn't come from the Julia community. He was a heavy user of Python and Fortran, calling Fortran from Python to speed up performance hotspots. He wrote this blogpost as his first introduction to Julia. So I think if it does cast numpy in a poor light, it was not done so intentionally to make it look bad.
- cycomanic 5y agoAnother thing to add, the python example code is a written in quite a weird (and in my opinion difficult to read way). n = len(a) M = np.exp(1jk(np.tile(a,(n,1))*2 + np.tile(a.reshape(n,1),(1,n))*2)) is equivalent to M = np.exp(1jk(a[:,None]*2 + a[None,:]*2)) which I find very easy to grasp.
- alanbernstein 5y agoA big improvement, but i'm not sure if indexing with None is very intuitive.
- occamrazor 5y agonp.newaxis instead of None is the idiomatic way
- eirki 5y agoGenuine question: Is Julia used for anything other than mathematics yet? Is there any sign of the Julia community expanding beyond science, maths, machine learning etc?
- jason0597 5y agoI have to ask to ask the obvious question: why? What problems would they be trying to solve?
- nerdponx 5y agoGradually-typed, syntactic macros, tidy syntax, good Unicode support in the standard library. All positive traits for app and/or server development in my opinion.
- nerdponx 5y agoDoes the Julia runtime have good properties for something like a Web server? Slow startup makes it questionable for CLI tools, although that is supposedly improving a lot. But what about high-concurrency "server-like" workloads? The language itself I think would be great for such an application, but I have no idea if the runtime itself would be good.
- oscardssmith 5y agoCurrently Julia will probably lose to C++ here due to some GC quirks and less well optimized strings. That said, both of these issues are being worked on, and I would expect fairly major progress by 1.8. (some of this work is already done. Specifically, as of 2 days ago memory alignment of strings has changed to save 8 bytes for most strings). Future improvements will likely include some form of small string optimization to prevent them from being heap allocated, and making julia's GC more efficient for highly threaded code.
- m_mueller 5y agoDoes it have to? IMO it's good to have a language focused on that domain as the tradeoffs tend to be different there, hence why Fortran is still actively used.
- rahimiali 5y agoYou can speed up the numpy code by 2x by replacing the line: M_old = np.exp(1j*k*(np.tile(a,(N,1))**2 + np.tile(a.reshape(N,1),(1,N))**2)) with a_squared = a ** 2 M = np.exp(1j * k * (a_squared[:, None] + a_squared[None, :])) This gets you to the same performance as single-threaded julia.
- dfdz 5y agoThis example is not really testing Fortran vs. Julia, but rather testing the speed of the math library you linked to. The most expensive part of the fortran code is evaluating sqrt and exp. For example on a 5600X the current Fortran code (with n=10 000) takes about 2.7 seconds. If I change the definition of M to remove the special functions (M(i,j) = a(i)+ a(j) then it takes .3 seconds. (Using gfortran with -Ofast) When the special functions are the issue, using the Intel Fortran compiler and Intel MKL will make the code about 2x faster (this is what Matlab or Julia is probably using)
- moelf 5y agoJulia ships with OpenBLAS, in some cases there are pure-Julia "blas-like" routine that can be as fast: https://github.com/mcabbott/Tullio.jl https://github.com/mcabbott/Tullio.jl
- celrod 5y agoOr much faster (at small sizes): https://github.com/JuliaLinearAlgebra/Octavian.jl https://github.com/JuliaLinearAlgebra/Octavian.jl
- eigenspace 5y agoAlso much faster at very large sizes :)
- celrod 5y ago`vsqrtpd` is an assembly instruction. `exp` and `sincos` are implemented in Julia. If you want to use a library to speed up the Julia code, checkout https://discourse.julialang.org/t/i-just-decided-to-migrate-from-python-fortran-to-julia-as-julia-was-faster-in-my-test/61188/19?u=elrod https://discourse.julialang.org/t/i-just-decided-to-migrate-... (but replace `@tvectorize` with `@tturbo`), which is code by eigenspace.
- dfdz 5y agoMy point is that for codes like this, the issue is not vectorization. Just evaluating the special functions exp and sqrt is taking 90% of the time. You can evaluate these special functions in parallel, but that is about it.
- wodenokoto 5y agoThe i used in the index Mij, is not the same i as in the multiplication ik? How does one make that out in the formula?
- gnufx 5y agoThe Fortran doesn't look quite equivalent, and we don't know how it was run, though that's irrelevant to the analysis except perhaps for OpenMP. As usual, it's a single number from a benchmark without understanding it, e.g. with -fopt-info. The general conclusion about Fortran invalid. It happens to be an example of a pathological vectorization case for GCC with an ancient issue open, sigh [1]. Much as it pains me to say so, apart from NAG's, I think, all the other compilers I know will vectorize such loops: ifort, xlf, flang, PGI, nvfortran (but the last three may be essentially the same here). Also, last time I tried it, -floop-nest-optimize (marked "experimental") was quite broken, again with an issue open either in the gcc or redhat tracker. -O3 does unroll-and-jam, at least. 1. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40770 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40770
- nxpnsv 5y agoIs gfortran considered a good fortran compiler? Back in my day, I think it still generated C code that then was compiled with gcc. I might be wrong, this was in the 90s...
- cycomanic 5y agoJust 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?
- kelsolaar 5y agoThe article is surprising as it illustrates the reason for switching to Julia with only a very simplistic single example. Surely there is more to that than this example especially considering that the Python implementation could be improved for readability and speed.
- johan_felisaz 5y agoI think most of the criticisms against Julia come from the fact that people see it as a Python competitor. In its actual state (even if the last version improved compile time a bit), it is not really usable as an exact python replacement (short scripts, opening a new interpreter each time). However it is a revolution for the HPC crowd. If your program is supposed to run for several days on multiple nodes, 10 seconds of compilation is a negligible price to pay for a much cleaner and shorter code than Fortran or C/C++. I've been using Julia for a few months for physics related simulations and it's just so much better than Fortran to work with.
- eigenspace 5y agoWhile I definitely agree, I'd say Julia is totally fine for short scripts too. Just spin up https://github.com/dmolina/DaemonMode.jl https://github.com/dmolina/DaemonMode.jl and you can launch lots of short scripts without excessively restarting julia. There's also PackageCompiler.jl https://github.com/JuliaLang/PackageCompiler.jl https://github.com/JuliaLang/PackageCompiler.jl for AOT compilation, but that's heavy enough that I wouldn't bother with it for short scripts.
- johan_felisaz 5y agoOf course, these packages are fantastic, they do help a lot for more experienced users. But for a pure beginner, there is a high risk he will give up Julia after 10s of Plots.jl compilation, way before we would hear about these. (and tbh, even leaving a REPL open all the time works perfectly fine, since include("myscript.jl") really does reload the script, unlike the import keyword in Python)
- eigenspace 5y agoYeah, that's fair. I guess I read your comment as saying that julia isn't a good replacement for Python, rather than it takes a tiny bit of learning when you're coming from other languages. I think the way we talk about julia sometimes makes Python and Matlab programmers think they can just take some of their old code and copy paste it into julia, switch around some keywords and have everything be faster. It's important to emphasize to these people that it's a different language with its own idioms that need to be learned.
- frr149 5y agoWhat makes Julia so fast?
- eigenspace 5y agoBeing designed to be fast. Lots of other high level languages were designed with the assumption that performance critical code would be written in low-level languages so they didn't bother designing the language to make various optimzations possible. Julia was designed for people who don't want to use a low level language for speed. It took a lot of great ideas from languages like Common Lisp, Fortran, etc. as well as a few good ideas of its own and packaged them together in a rather novel way. The language is dynamic, but it's dynamism is limited in specific ways that makes it very easy for the compiler to optimize code.
- jakobnissen 5y agoFunctions compile just-ahead-of-time to native code using LLVM. Functions specialize based on the input types used and use type inference, meaning that in idiomatic Julia code, all types are known at compile time. Finally, Julia uses struct layout similar to static languages like C or Rust. The trifecta of complete type information, compilation to native code, and efficient data layout, is what makes the difference. Notably, Julia is not any faster than most static languages, nearly all of whom also use the same trio.
- woeirua 5y agoCan someone explain how Julia MT got a super-linear improvement in performance by using 4 cores + HT vs 1 core? Fortran’s MT performance is more along the lines of what I would expect.
- walshemj 5y agoQuestion is which Fortran vs Which Version of Julia
- crazypython 5y agoNumba exists, which is a vectorizing, optimizing JIT compiler for a subset of Python that uses NumPy. You simply install it and add a decorator. https://numba.pydata.org https://numba.pydata.org
- agucova 5y agoThe problem is that Numba's subset is really limited and often means you have to spend a significant time investment on making your code compatible, often while decreasing readability or doing unnecessary type conversions. Numba is very cool, but it's inherently limited.
- kilodeca 5y agoIt's too hard to read on mobile device.
- aimor 5y agoIs the python code missing a square root? https://github.com/mdmaas/julia-numpy-fortran-test/blob/main/test.py https://github.com/mdmaas/julia-numpy-fortran-test/blob/main... For fun I ran in Matlab with a 2.9 GHz i7-7820HQ and get about 1.83s for N=10,000 single threaded. A = exp((k*1i)*sqrt(a.^2 + (a.^2)'))
- 1vuio0pswjnm7 5y agoWhich interpreted language has the fastest startup time.