[LinearSolver] simple improvement of about 15% - #6272
Conversation
alxbilger
left a comment
There was a problem hiding this comment.
Thank you for this contribution! 15% is definitively something.
I understand the changes, but I was more curious about the method to detect the locations where this kind of optimization is useful.
I'll make a performance comparison on my hardware.
You were right to investigate the linear solver. It's usually one of the bottlenecks. The linear solver has 2 important steps: 1) it factorizes the matrix, 2) it solves a linear system using the factorization. The bottleneck depends on how much linear systems to solve, but as a general rule, both of them are important. The code is based on the CSparse library. There are also other solvers based on the Eigen library. To finish, I think that SparseLDLSolver can be faster if it would work with blocks (3x3 for example) instead of scalars. Currently, we convert the matrix from blocks to scalars, but I guess the factorization would be faster using blocks. This is something to try, but it's more an algorithmic optimization.
|
|
||
| // avoid explicitely putting loop exit conditions in the loop | ||
| // the compiler optmiser nor the chip can know they've not changed so they mostly pesimistically re-evaluate. | ||
| const auto d { data->n }; |
There was a problem hiding this comment.
I am curious: did you use the compiler report to detect that? Or it's just a general rule?
There was a problem hiding this comment.
It's a general rule. I'll try to remember some references (eg Fedor Pikus). If there isn't an obvious way for the compiler or processor to definitively know that the contents of the loop haven't changed the value, it has to pessimistically re-evaluate every iteration. The usual one is size() on a container, the compiler isn't allowed to guess that contents of the loop probably haven't changed the container length so it has to call it every time. If the developer knows that it hasn't changed it's best to just not leave the compiler guessing. It doesn't always make much difference, but if it can just put the value in a register and forget about it, all the better.
| auto v3{ value }; | ||
| auto v4{ value }; | ||
|
|
||
| for (; k < k1; k += 4) |
There was a problem hiding this comment.
Does the loop unrolling parameter 4 depend on the compiler/hardware?
There was a problem hiding this comment.
It's the minimal vector length for SSE registers, 4 double words, so on the off chance the compiler vectorises it... But also the number of pipelines that might be available for floating point maths. My hardware is a bit old which is good to exaggerate performance issues, but if the destination hardware is more modern other numbers might be faster. I tried 8 but it slowed down a bit, though not much.
I have a vectorised version that raised the performance gain up to 20%, but is AVX2 specific and I don't know what your hardware requirements are. Also Windows builds a slightly different for writing vector code and I thought I'd keep it simple in case I was optimising a backwater that nobody was interested in ;-)

Hi,
I am a performance engineer with some time on my hands.
Here is a simple change that, on my hardware, gives about 15% performance improvement for fallingBeamLagrangianCollision.scn.
It rests on working more sympathetically with he hardware by reducing the loop carried dependencies, allowing the chip to pipeline more effectively.
I have more complicated versions that are faster but they start depending on chip features and I don't know what your supported hardware base is
Hopefully this is useful. If you direct me towards more important areas of concern I can have a look.
Please forward any questions or comments.
The job I was focussing on was this: 'build/bin/runSofa_d -g batch -n 1000 -c 100 examples/Demos/fallingBeamLagrangianCollision.scn'
By submitting this pull request, I acknowledge that
I have read, understand, and agree SOFA Developer Certificate of Origin (DCO).
Reviewers will merge this pull-request only if