fix: don't rebuild the linear solver on every inline linear SCC solve - #160
ChrisRackauckas merged 5 commits into
Conversation
ee69471 to
f6accf0
Compare
|
You're right. I swept SCC sizes and going straight to It gives up the default's backend switch ( One steer I need: where should the cache live? In the expression it ends up shared across Two things I hit prototyping: |
|
I think keeping it task local would be cleaner. Do you have an example sketch of what you're running into with the factorization getting shared? Ideally we would want to re-use as much as we can, while remaining correct and fast. |
|
Sketch: with one cache shared by everything and 8 threads each running its own problem from the same system, 18289 of 24000 results come back wrong. Task local gives 0, same as main today. The Gone with task local then. One cache per eltype and size, and since two solve sites of the same shape can share one, I copy the solution back into Allocations are flat at 112 B / 3 regardless of size now, and it's faster than main throughout: 109 vs 173 ns at N=2, 373 vs 557 at N=8, 4180 vs 5186 at N=32. Checked that the cached path still picks the same factorization as the uncached one, GenericLU at m=4 and AppleAccelerateLU at m=32. |
Closes #157.
safe_ldivwas building a freshLinearProblemandLinearCacheon every call, which is 23 allocations per solve no matter how small the system is. It now keeps theLinearCachein task local storage and reuses it, so a steady state call neither builds aLinearProblemnor allocates a factorization.One cache per eltype and size. Two solve sites of the same shape can share one, so the solution gets copied back into
b, which belongs to that site alone, rather than handing back the cache's own buffer. The lookup is necessarily type unstable so the solve sits behind a function barrier.Task local rather than kept alongside the system because the emitted expression is shared by every problem built from it, while
Aandbare not. With one shared cache and 8 threads on their own problems, 18289 of 24000 results come back wrong; task local gives 0.The new values are filled into the cache's own buffers and then assigned back rather than written through
cache.Ain place, so that LinearSolve runs its invalidation. Writing in place keeps the partials of the first solve and silently returns stale derivatives under ForwardDiff on every later call, with the values still correct. There's a test for it now.Allocations are flat at 112 B / 3 per RHS call regardless of size, and it's faster than main throughout: 144 vs 173 ns at N=2, 392 vs 557 at N=8, 4438 vs 5186 at N=32. The cached path still picks the same factorization as the uncached one, GenericLU at m=4 and AppleAccelerateLU at m=32.