Grassmann.jl A\b 3x faster than Julia's StaticArrays.jl
discourse.julialang.org
discourse.julialang.org
Calculating determinants is notoriously prone to cancellation error if done naively, so you've got to be really careful. By extension, the usual text book implementation of Cramer's rule is a rather bad way to solve matrix systems. (I don't know much about Grassman.jl so perhaps they've used some known tricks to make this work better, but you do have to be really careful with this stuff.)
Actually in early versions of StaticArrays we were pretty cavalier about numerical accuracy because it was just a library we needed for fast geometric calculations. However as people started using this library for serious numerical work we've had to pay a lot more attention to having good numerical properties.
There's still a lot to learn - and no doubt much work to do - in upgrading the implementation to make sure everything is first robust, and second as fast as possible.
To be clear, I'm not claiming there's anything wrong with your algorithm but simply adding a note of caution. Any careful comparison between methods must include both efficiency and numerical accuracy.
Here's the post from chris:
"That’s not type-stable. x = [@SMatrix randn(5,5) for i in 1:10000] is a type stable way to compute an array of random SMatrices, and that nearly doubles the speed of the computation and removes the allocations down to 2, i.e. from:
2.549 ms (29496 allocations: 1.44 MiB) to
1.469 ms (2 allocations: 390.70 KiB) for me. Still a nice performance for Grassmann.jl, but seems to be <2x."
The deal with 1.5 is that the GC has improved in that it does not require structs with references to dynamically allocated objects to be heap allocated themselves.
It can be a bit annoying, and can result in much less readable code (eg having to explicitly write things like mul!(C, A, B)). It ends up looking a lot more like the FORTRAN it is meant to replace, and if you aren't careful you lose the ability to use generic types, though multiple dispatch is a great solution. The worst case I have found is iteratively calling linear algebra solvers (Eig, SVD, LU) which do not have any option for preallocating and reusing work arrays. The only way to do this now is to call the BLAS/LAPACK routines directly with ccall, which is a huge pain. There was an attempt to fix this with PowerLAPACK.jl, but it seems abandoned, so for the moment writing optimized methods in FORTRAN/C and calling from Julia is sometimes still preferable.
Julia does quite well though, so this only seems necessary for things like core NLA libraries, for example I would not try rewriting ARPACK in Julia for a performance gain. The gain in flexibility for writing these in Julia is absolutely huge though, and I definitely recommend it. The Grassmann.jl library has many examples of what a language like Julia makes possible, or DifferentialEquations.jl.
[1] LoopVectorization: https://github.com/chriselrod/LoopVectorization.jl Announcement post and discussion: https://discourse.julialang.org/t/ann-loopvectorization/3284...
Pure julia ARPACK already exists, e.g. https://github.com/haampie/ArnoldiMethod.jl/.
A competive BLAS-gemm is implemented here https://github.com/YingboMa/MaBLAS.jl/blob/master/src/gemm.j... (single-threaded).
A LAPACK-like library could be https://github.com/JuliaLinearAlgebra/GenericLinearAlgebra.j...
I don't know how well ArnoldiMethod.jl compares with ARPACK, but if there is a gap my suggestion is simply that these recent developments might help bridge it :)
Not sure what you mean, ARPACK just wraps a bunch of calls to LAPACK and BLAS, that's all. It does not have any low-level linalg kernels of its own. Also, its main bottleneck is typically outside of the algorithm, namely matrix-vector products. ArnoldiMethod.jl is pretty much a pure Julia implementation of ARPACK without any dependency on LAPACK (only BLAS when used with Float32/Float64/ComplexF32/ComplexF64). Finally note that you can easily beat ARPACK by adding GPU support, since they don't provide it.
I'm surprised if this was inherent to StaticArrays.jl though. Probably some interop edge case, I'm imagining.
Shall we summon Chris Rackauckas?
That said, the backing storage for the particular static array types used in this example (SMatrix,SVector) is an immutable Tuple which should generally be stack allocated if the library and optimizer are working together as intended. So the allocations here are a bit of a surprise; they may indicate a lack of inlining in something we expected to be inlined.
I’m increasingly impressed by Julia. It’s a crowded space and they remain relevant. Maybe even best of class for some applications.
I have generated SIMD code that outperforms C and C++, even after some hand optimizations. As a matter of fact, some people are re-writing a BLAS drop-in replacement in pure Julia, and the performance is not going to be too far off from BLAS [1]. If you work in scientific computing, you can probably appreciate how amazing that is.
The combination of multiple dispatch, a simple type system and an LLVM backend is wonderful. In the long run, Julia will probably grab lots of Python's and R's marketshare because having a single language instead of two languages (Python + Cython/Pythran/... or R + C++) is a huge advantage.
[1] https://discourse.julialang.org/t/we-can-write-an-optimized-...
I didn't appreciate the importance of this (or the degree to which this just works as a result of multiple dispatch), but there's a good talk about this from JuliaCon 2019 [1] by Stefan Karpinski
No operator overloading, and scientists ain't gonna look twice at anything without that
Because the code is written in solid, idiomatic Julia, it's interoperable with tons of other libraries. I actually had success using this library with CuArrays.jl without any modifications. So yeah, you can use it to run geometric algebra computations on a GPU for algebras of dimension < 2^5 (in my case, I tried it with CGA3D).
But, I think Julia is the closest thing to an honest-to-goodness lisp that has a prayer of mainstream adoption. I quite like Julia and think the community has leveraged the meta programming strengths of the language well enough to convince me to relinquish my love of minimalist lisp syntax.
I do wonder if the biggest risk to Julia isn’t that it’s not extreme / superlative in any dimension. If you want raw performance, Rust seems to be capturing the lion’s share of excitement among “new” languages. If you want meta programming, Julia still feels like a compromise compared to Scheme/Racket/Clojure/Common Lisp. If you want libraries, Python is still king. If you want stats libraries, R is still king. Julia is impressively good at all of those things... but it’s not good enough to be the most compelling of any of them.
Still, it makes me think that it may be time to dive in head first rather than dabble my toes as I've been doing for the past few years. I guess my main barrier at the moment is compiler / deployment story—it still seems hard to build a deployable binary that fits in, say, a Lambda layer as far as I can tell.
I guess the selling point is that being good at many things means you don't have to keep switching between all these languages, or glueing them together. Others have mentioned the composability benefits of this. But it also has a learning advantage, I think. You can incrementally learn to speed up the crucial piece of something. You can learn just enough metaprogramming to solve some problem you have.
Edit: This post also does not mention numerical stability of the Grassmann.jl method, which is a real concern in practice, even for such small systems.
You see this kind of math in textbooks focused on computer graphics.
Regardless the name Grassman is tied to exterior algebras (and therefore geometric algebras), which are implemented by this library and which can be used to solve matrix equations (although I couldn't recommend using the geometric algebra approach for higher dimensions, unless you can do so symbolically).
The road to reality by Roger Penrose also has a treatment of Grassmann algebra in physics context.
I agree the library is likely a reference to the mathematician.
Like I said it's nothing terribly important but it's still an interesting difference.