Let’s be specific here: the Poisson equation I consider as test case is the 2D Poisson equation on the unit square (or rectangle) discretized with second-order accurate central finite differences on a uniform mesh.
Do basic iterative methods work well? (Jacobi, Gauss-Seidel, SOR) – They work, for sure. Do they work well ? It kind of depends. I would never use Jacobi or Gauss-Seidel as actual solvers outside of a pedagogical objective. While they are very simple to implement, they are simply too slow. On the blog post, you can see that for a 512² grid, the fastest Jacobi takes 16 seconds to solve the system. Once. This is not practical in any way if you have to solve this equation repeatedly like in an incompressible Navier-Stokes simulation.
SOR, that is a different story. For this particular discretized equation, I think it has one of the best trade-off between code complexity and computational performance. Code-wise, it is basically a minor variation of Gauss-Seidel, so very easy to implement. Performance-wise, it is way more efficient. Even more so for this particular test case since you actually have a closed-form expression for the optimal relaxation weight. There are some caveats though:
- Just like Gauss-Seidel, SOR suffers from the same loop-carried dependency. It’ll thus be harder for the compiler to optimize the code. But at the same time, the method requires far fewer iteration to converge. So, trade off again.
- If you run a 3D case (or even a big 2D case) and have to implement the linear solver yourself, SOR can be a pain to parallelize.
If you want to stay in Jacobi-land in terms of solver complexity, but still have pretty good performances and relatively straightforward parallelization, Chebyshev-Jacobi is in my opinion your best alternative. You get convergence rate somewhat similar to SOR but retain the simplicity of Jacobi. And it might actually be more computationally efficient than SOR (even though it requires more iterations) precisely because the compiler can vectorize the code easily. Another important feature is that, except for periodic computation of the norm of the correction vector, no inner product is required and thus no all_reduce in parallel which would force a synchronization of the different processes.
How about Krylov methods ? (Conjugate Gradient, GMRES) – I’ll consider only CG since the test case is a symmetric positive definite operator. You’ll have to be slightly more careful when implementing it, but it ain’t that complicated. And it’ll converge a lot faster than Jacobi or Gauss-Seidel. It also turns out to be very closely related to the Chebyshev-Jacobi method (the two can essentially be derived in the same way).
I’ve just ran a quick test case with LightKrylov, and basically it converges in roughly 1000 iterations. That is 1000 matrix-vector products, compared to the 74 000 / 138 000 needed for Gauss-Seidel/Jacobi. So even if a single iteration may be slightly more costly (in particular because it involves a handful of inner products), time-to-solution is well under a second. With a block-Jacobi preconditionner, the number of iterations drops around 500, so even faster. And in general, having a good preconditionner is key for maximum computational performance but is very case-dependent.