Skip to content

[LinearSolver] simple improvement of about 15% - #6272

Open
stu-l wants to merge 2 commits into
sofa-framework:masterfrom
stu-l:sparsesolver-perf-experiment
Open

[LinearSolver] simple improvement of about 15%#6272
stu-l wants to merge 2 commits into
sofa-framework:masterfrom
stu-l:sparsesolver-perf-experiment

Conversation

@stu-l

@stu-l stu-l commented Aug 28, 2026

Copy link
Copy Markdown

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

  • it builds with SUCCESS for all platforms on the CI.
  • it does not generate new warnings.
  • it does not generate new unit test failures.
  • it does not generate new scene test failures.
  • it does not break API compatibility.
  • it is more than 1 week old (or has fast-merge label).

@alxbilger alxbilger left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious: did you use the compiler report to detect that? Or it's just a general rule?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the loop unrolling parameter 4 depend on the compiler/hardware?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ;-)

@alxbilger alxbilger added pr: enhancement About a possible enhancement pr: status to review To notify reviewers to review this pull-request labels Aug 28, 2026
@alxbilger alxbilger changed the title simple improvement of about 15% [LinearSolver] simple improvement of about 15% Aug 28, 2026
@stu-l

stu-l commented Aug 28, 2026

Copy link
Copy Markdown
Author

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.

Detection method; I asked chatgpt where you might want effort applied, I think it suggested the fallingBeamLagrangianCollision.scn job. So I ran it through VTune profiler which I have been using for the past ~20yrs for profiling code running on Intel chips. It is very good to work out where and why the chip is being stressed.
My specialism is profiling and optimising C++. I can't comment on the maths you mention above, but I can comment on how the hardware responds to the code.

My hardware is twin Xeon CPU E5-2603 - they are old. But they usefully exaggerate performance problems. But also if they run optimised code then almost any other chip will.

These are your top three problems. I'm just working on the second:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr: enhancement About a possible enhancement pr: status to review To notify reviewers to review this pull-request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants