Determinant and Inverse return wrong values when called from more than one thread
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 35/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- À l'abandon
- Stack technique
- csharp
- Domaine
- performance
Piste de recherche
Commencez par SquareMatrixFactory<T, TWrapper>.GetMatrix et ExpressionCompiler<T, TWrapper>.storage, puis reproduisez les échecs de déterminant/inversion signalés avec Parallel.For, y compris avec un cache froid. Le travail est terminé lorsque les opérations matricielles concurrentes renvoient les valeurs vérifiées séquentiellement, sans exceptions de collection ni corruption des tampons partagés.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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.
- Langage dominant
- C#
- Étoiles
- 52
- Forks
- 6
- Métriques de merge des PR
- Aucune PR mergée en 30 j
Préparer son environnement
Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de ASC-Community/GenericTensor
-
Null reference warnings to fixOuvertegood first issue
Difficulté 4/5 3-5 jours Accessibilité débutants 25/100
-
proposal
Difficulté 4/5 3-5 jours Accessibilité débutants 35/100
ASC-Community/GenericTensor#36 · 1 commentaire ·
-
Opinions wanted proposal
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 25/100
ASC-Community/GenericTensor#31 · 2 commentaires ·
-
Will this package support MKL or OpenBlas as backend to accelerate matrix inverse computing speedOuverte
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 25/100
ASC-Community/GenericTensor#30 · 1 commentaire ·
-
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 20/100
ASC-Community/GenericTensor#29 · 1 commentaire ·
Toutes les issues de ASC-Community/GenericTensor
Issues similaires
-
bug priority: P2
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
Parameterless constructor is not a must-provided by user when a struct has a field initializerOuverte:watch: Not Triaged dotnet-csharp/svc fundamentals/subsvc
Difficulté 1/5 Moins d'une heure Accessibilité débutants 88/100
Les mainteneurs répondent en général sous 1 jour
-
Wrong values of Kernel32.FileShare.Delete and Kernel32.FileAttributes.PosixSemanticsPeut-être pris @ElectLive l’a pris aujourd’hui. Ouvertebug good first issue help wanted
Difficulté 1/5 Moins d'une heure Accessibilité débutants 83/100
ForNeVeR/TruePath#241 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
files-community/Files#19047 ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour