Determinant and Inverse return wrong values when called from more than one thread
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 35/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Ferma
- Stack tecnologico
- csharp
- Ambito
- performance
Direzione di ricerca
Inizia con SquareMatrixFactory<T, TWrapper>.GetMatrix e ExpressionCompiler<T, TWrapper>.storage, quindi riproduci i fallimenti riportati di determinante/inversa con Parallel.For, includendo una cache fredda. Il lavoro è completato quando le operazioni concorrenti sulle matrici restituiscono i valori verificati sequenzialmente senza eccezioni sulle raccolte né corruzione dei buffer condivisi.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Two process-wide statics are read and written with no synchronisation, so matrix operations called from more than one thread return wrong numbers. Nothing throws, nothing is malformed, and no result looks suspicious.
I found this from the other side: AngouriMath uses GenTensor for its symbolic matrices, and a determinant computed on a background thread was coming back numerically wrong. AngouriMath's issue is #1219.
1. The scratch-matrix pool
SquareMatrixFactory<T, TWrapper>.GetMatrix hands every caller the same GenTensor for a given size, and the caller then writes into it — Inversion.GetCofactorMatrix(t, temp, ...) fills the matrix it is given. Two threads taking a determinant or an inverse of the same size get the same buffer and overwrite each other's minors between the write and the read.
Sixty 5×5 int matrices, each computed once sequentially for truth and then rebuilt and recomputed under Parallel.For:
DeterminantLaplace 53 of 60 disagree
Adjoint 60 of 60 disagree
Around 10 ms, every run. This is not a rare interleaving, it is the normal case.
DeterminantGaussianSafeDivision is unaffected — it works on its own copy and never asks the pool for anything.
The lock inside GetMatrix does not help and is itself incomplete: the list is indexed after the lock is released, so a concurrent Add can reallocate the backing array underneath the reader.
2. The compiled-operation cache
ExpressionCompiler<T, TWrapper>.storage is a plain Dictionary, written by whichever thread first asks for a given (operation, rank, parallel). Dictionary<,> is documented as safe for concurrent readers only while nobody is writing.
Reached cold from several threads it fails with the runtime's own diagnostic:
System.InvalidOperationException: Operations that change non-concurrent collections must
have exclusive access. A concurrent update was performed on this collection and corrupted
its state. The collection's state is no longer correct.
...
The given key '(Subtraction, 1, False)' was not present in the dictionary.
Worth noting that this one hides easily. My first test computed its expected values sequentially and only then went parallel, which populated every key before the threads started — the cache holds a handful of entries, so a warm-up makes the defect invisible and the test passes against the broken code.
Fix
I have opened a PR. Both are small: [ThreadStatic] for the pool, since a scratch buffer has nothing to share between threads, and ConcurrentDictionary for the cache. The Laplace determinant comes out about 5% faster, because the pool is now taken once at the top of the recursion instead of re-checked and re-indexed at every one of its n! nodes.
- Lingua principale
- C#
- Stelle
- 52
- Fork
- 6
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ASC-Community/GenericTensor
-
good first issue
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
-
proposal
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
ASC-Community/GenericTensor#36 · 1 commento ·
-
Opinions wanted proposal
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
ASC-Community/GenericTensor#31 · 2 commenti ·
-
Will this package support MKL or OpenBlas as backend to accelerate matrix inverse computing speedAperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
ASC-Community/GenericTensor#30 · 1 commento ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
ASC-Community/GenericTensor#29 · 1 commento ·
Tutte le issue di ASC-Community/GenericTensor
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
Volodymyr-Petrunin/Bankomaten#45 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
AvaloniaUI/Avalonia#22420 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
dotnet/SqlClient#4823 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
type/automation type/tech-debt
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
area-testing p1-recommended
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno