Quand est-il acceptable d'utiliser des tableaux parallèles?

14

J'ai rencontré du code (nouveau code) qui utilise ce que j'appelle des «tableaux parallèles» ou des listes. Cela signifie qu'il y a 2 tableaux qui contiennent des données liées et qui sont liés par leur position (index) dans le tableau.

Je trouve cela déroutant et sujet à toutes sortes d'erreurs. La solution que je propose normalement est de créer un objet appelé Companyavec les champs CompanyId et CompanyName.

Un exemple bien réel:

List<string> companyNames;
List<int> companyIds;

//...They get populated somewhere and we then process

for(var i=0; i<companyNames.Count; i++)
{
    UpdateCompanyName(companyIds[i],companyNames[i]);
}

Ces tableaux parallèles sont-ils considérés comme une mauvaise pratique ?

GER
la source
9
Simplement une preuve supplémentaire qu'aucune langue n'a été inventée dans laquelle vous ne pouvez pas écrire Fortran.
andy mango
3
Il peut y avoir des avantages de mise en cache (assez importants) à faire quelque chose comme ça (bien que vous ayez besoin de tableaux contigus et non de listes liées), et cela est devenu quelque peu populaire dans la programmation de jeux liée à la "conception orientée données". Cependant, cela ne semble pas s'appliquer à votre cas. Il ne semble pas que vous créiez du code critique pour les performances.
Derek Elkins a quitté SE le
2
@DerekElkins ... Intéressant que votre commentaire en suive un comparant cela au code Fortran. Les premières versions de Fortran ne prenaient pas en charge les structures définies par l'utilisateur, et même après son ajout, le code Fortran idiomatique utilise plusieurs tableaux de propriétés et non des tableaux de structures. Et cela est souvent crédité comme une des raisons pour lesquelles le Fortran est souvent considéré comme la langue la plus rapide.
Jules
3
Une pensée tangentielle à cette question: de nombreux langages fonctionnels encouragent activement le travail avec de telles listes. Ils ont une fonction, généralement appelée zip, qui les convertit en une liste de tuples. Votre code ressemble à C #. La dernière version de C # a ajouté la prise en charge des tuples de première classe. Je me demande si, par conséquent, ils ont ajouté une fonction zip quelque part qui pourrait mettre vos listes dans une structure utile pour vous automatiquement?
Jules
4
Eh bien, il y a parfois des raisons d'utiliser intentionnellement deux tableaux, mais dans 99% de tous les cas, je l'ai vu, la seule raison était la paresse de l'auteur d'origine pour introduire une structure de données englobante.
Doc Brown

Réponses:

23

Voici quelques raisons pour lesquelles quelqu'un pourrait utiliser des tableaux parrel:

  1. Dans un langage qui ne prend pas en charge les classes ou les structures
  2. Pour éviter le verrouillage des threads lorsque les threads individuels ne modifient qu'une des colonnes
  3. Lorsque la méthode de persistance force ces éléments à être stockés séparément et que vous les reconstituez.
  4. Ils peuvent consommer moins de mémoire si les structures sont rembourrées. (non applicable pour ces types de données en C #)
  5. Lorsque certaines parties des données doivent être rapprochées pour utiliser efficacement le cache du processeur (ne serait pas utile dans le code ci-dessus).
  6. Utilisation de codes d'opération SIMD (Single Instruction Multiple Data). (non applicable pour ce code, ou chaînes du tout)

Je ne vois aucune raison impérieuse de le faire dans ce cas ... et il y a probablement de meilleures options dans tout ce qui précède ou ne sont pas si utiles dans un langage de haut niveau.

TheCatWhisperer
la source
3
Ils peuvent également consommer moins de mémoire si les structures sont rembourrées. Plusieurs grands tableaux, alloués intelligemment, peuvent consommer moins de mémoire qu'un tableau de structures.
Frank Hileman
4
4. Lorsque des parties des données doivent être rapprochées pour utiliser efficacement le cache du processeur. (Nécessaire dans de rares cas.)
Blrfl
@ Frank Hileman, Whilie Je pense que la réponse de TheCatWhisperer est complètement correcte, votre commentaire est, en fait, la meilleure raison de choisir cette approche. Si la consommation de mémoire est critique, la surcharge de mémoire sur le remplissage des structures peut être importante, surtout si de grands nombres sont en jeu.
Vladimir Stokic
Ajout de vos suggestions à la réponse
TheCatWhisperer
Re (2), comment ça? Je peux écrire un programme avec un seul tableau de structures et un verrou par champ aussi facilement que je peux en écrire un avec plusieurs tableaux et un verrou par tableau.
Solomon Slow
7

J'ai été coupable d'utiliser des tableaux parallèles . Parfois, vous êtes tellement plongé dans la structure que vous ne voulez pas penser à la résumer. L'abstraction peut être un peu plus difficile à refactoriser, vous êtes donc réticent à vous lancer directement jusqu'à ce que vous ayez prouvé ce dont vous avez vraiment besoin.

À ce stade, il vaut la peine d'envisager une refactorisation pour résumer les détails. Souvent, la principale raison pour laquelle j'hésite à le faire, c'est qu'il est difficile de penser à un bon nom.

Si vous pouvez voir un bon moyen d'abstraire des tableaux parallèles, faites-le à chaque fois. Mais ne vous paralysez pas en refusant de les toucher. Parfois, un petit code sale est le meilleur tremplin vers un excellent code.

candied_orange
la source
6

Ce modèle est parfois également appelé structure de tableaux (par opposition à tableau de structures), et est extrêmement utile lors de la vectorisation de code. Plutôt que d'écrire un calcul qui s'exécute sur une seule structure et d'en vectoriser des bits, vous écrivez le calcul comme vous le feriez normalement, sauf avec les éléments intrinsèques SSE afin qu'il s'exécute sur 4 structures au lieu d'une. C'est généralement plus facile et presque toujours plus rapide. Le format SoA rend cela très naturel. Il améliore également l'alignement, ce qui accélère les opérations de mémoire SSE.

Dan
la source
Oui, cette approche est utilisée lors de l'apprentissage automatique sur le GPU. Il est habituel de séparer les champs de nombreux exemples distincts, de regrouper toutes les valeurs de chaque champ dans un tenseur distinct et de transmettre ces tenseurs pour qu'ils soient calculés en bloc afin de produire une liste de prédictions.
Rétablir Monica le