Disons que vous devez avoir une liste / un tableau d'entiers que vous avez besoin d'itérer fréquemment, et je veux dire extrêmement souvent. Les raisons peuvent varier, mais disons que c'est au cœur de la boucle la plus interne d'un traitement à volume élevé.
En général, on opterait pour l'utilisation de Lists (List) en raison de leur flexibilité de taille. En plus de cela, la documentation msdn affirme que les listes utilisent un tableau en interne et devraient fonctionner tout aussi rapidement (un coup d'œil rapide avec Reflector le confirme). Néanmoins, il y a des frais généraux impliqués.
Quelqu'un a-t-il réellement mesuré cela? itérer 6 millions de fois dans une liste prendrait-il le même temps qu'un tableau?
T[]
vsList<T>
peut faire une grande différence de performance. Je viens d'optimiser une application extrêmement intensive en boucles (imbriquées) pour passer des listes aux tableaux sur .NET 4.0. Je m'attendais peut-être à une amélioration de 5% à 10%, mais j'ai eu plus de 40% d'accélération! Aucun autre changement que de passer directement d'une liste à l'autre. Toutes les énumérations ont été faites avec desforeach
déclarations. Sur la base de la réponse de Marc Gravell, il ressembleforeach
avecList<T>
est particulièrement mauvaise.Réponses:
Très facile à mesurer ...
Dans un petit nombre de code de traitement en boucle serrée où je sais que la longueur est fixe, j'utilise des tableaux pour ce tout petit peu de micro-optimisation; les tableaux peuvent être légèrement plus rapides si vous utilisez le formulaire indexeur / for - mais l'IIRC pense que cela dépend du type de données dans le tableau. Mais à moins que vous n'ayez besoin de micro-optimiser, restez simple et utilisez,
List<T>
etc.Bien sûr, cela ne s'applique que si vous lisez toutes les données; un dictionnaire serait plus rapide pour les recherches basées sur les clés.
Voici mes résultats en utilisant "int" (le deuxième nombre est une somme de contrôle pour vérifier qu'ils ont tous fait le même travail):
(modifié pour corriger le bogue)
basé sur le banc d'essai:
la source
Résumé:
Array doit utiliser:
Liste à utiliser:
LinkedList doit utiliser:
Si nécessaire, ajouter des cellules au début / au milieu / à la fin de la liste (souvent)
Si nécessaire, uniquement un accès séquentiel (avant / arrière)
Si vous devez enregistrer de GRANDS éléments, mais que le nombre d'éléments est faible.
Mieux vaut ne pas l'utiliser pour une grande quantité d'éléments, car il utilise de la mémoire supplémentaire pour les liens.
Plus de détails:
Beaucoup plus de détails:
https://stackoverflow.com/a/29263914/4423545
la source
Je pense que la performance sera assez similaire. La surcharge qui est impliquée lors de l'utilisation d'une liste par rapport à un tableau est, à mon humble avis, lorsque vous ajoutez des éléments à la liste et lorsque la liste doit augmenter la taille du tableau qu'elle utilise en interne, lorsque la capacité du tableau est atteinte.
Supposons que vous ayez une liste avec une capacité de 10, alors la liste augmentera sa capacité une fois que vous voudrez ajouter le 11e élément. Vous pouvez réduire l'impact sur les performances en initialisant la capacité de la liste au nombre d'éléments qu'elle contiendra.
Mais, pour savoir si l'itération sur une liste est aussi rapide que l'itération sur un tableau, pourquoi ne pas le comparer?
Sur mon système; l'itération sur le tableau prenait 33 ms; itérer sur la liste a pris 66 ms.
Pour être honnête, je ne m'attendais pas à ce que la variation soit autant. Donc, j'ai mis mon itération en boucle: maintenant, j'exécute les deux itérations 1000 fois. Les résultats sont:
Maintenant, la variation n'est plus si grande, mais quand même ...
Par conséquent, j'ai démarré .NET Reflector et le getter de l'indexeur de la classe List ressemble à ceci:
Comme vous pouvez le voir, lorsque vous utilisez l'indexeur de la liste, la liste vérifie si vous ne sortez pas des limites du tableau interne. Ce chèque supplémentaire a un coût.
la source
si vous n'obtenez qu'une seule valeur de l'un ou l'autre (pas dans une boucle), alors les deux vérifient les limites (vous êtes dans le code managé, rappelez-vous), c'est juste la liste qui le fait deux fois. Voir les notes plus tard pour savoir pourquoi ce n'est probablement pas un gros problème.
Si vous utilisez le vôtre pour (int int i = 0; i <x. [Length / Count]; i ++), la principale différence est la suivante:
Si vous utilisez foreach, la principale différence est la suivante:
La vérification des limites n'est souvent pas un problème (surtout si vous êtes sur un processeur avec un pipeline profond et une prédiction de branche - la norme pour la plupart de nos jours), mais seul votre propre profilage peut vous dire si c'est un problème. Si vous êtes dans des parties de votre code où vous évitez les allocations de tas (les bons exemples sont des bibliothèques ou des implémentations de hashcode), vous assurer que la variable est typée comme List pas IList évitera cet écueil. Comme toujours profil si cela compte.
la source
[ Voir aussi cette question ]
J'ai modifié la réponse de Marc pour utiliser des nombres aléatoires réels et faire le même travail dans tous les cas.
Résultats:
Compilé comme version sous VS 2008 SP1. Exécution sans débogage sur un [email protected], .NET 3.5 SP1.
Code:
la source
Les mesures sont bonnes, mais vous obtiendrez des résultats très différents en fonction de ce que vous faites exactement dans votre boucle intérieure. Mesurez votre propre situation. Si vous utilisez le multi-threading, cela seul est une activité non triviale.
la source
En effet, si vous effectuez des calculs complexes à l'intérieur de la boucle, les performances de l'indexeur de tableau par rapport à l'indexeur de liste peuvent être si marginales que finalement, cela n'a pas d'importance.
la source
En voici un qui utilise des dictionnaires, IEnumerable:
la source
N'essayez pas d'ajouter de la capacité en augmentant le nombre d'éléments.
Performance
la source
J'avais peur que les Benchmarks publiés dans d'autres réponses ne laissent encore de la place au compilateur pour optimiser, éliminer ou fusionner les boucles, alors j'en ai écrit une qui:
Le résultat est qu'un tableau direct a des performances environ 250% meilleures qu'un accès à un tableau encapsulé dans un IList:
Voici le code:
la source
Puisque List <> utilise des tableaux en interne, les performances de base doivent être les mêmes. Deux raisons pour lesquelles la liste pourrait être légèrement plus lente:
Pour vérifier si cela fait une différence pour vous, il est probablement préférable d'ajuster les fonctions de chronométrage publiées à une liste de la taille que vous prévoyez d'utiliser et de voir comment sont les résultats pour votre cas particulier.
la source
Depuis que j'avais une question similaire, cela m'a permis de démarrer rapidement.
Ma question est un peu plus précise, `` quelle est la méthode la plus rapide pour une implémentation de tableau réflexif ''
Les tests effectués par Marc Gravell montrent beaucoup de choses, mais pas exactement le timing d'accès. Son timing inclut également le bouclage sur les tableaux et les listes. Depuis que j'ai également proposé une troisième méthode que je voulais tester, un «dictionnaire», juste pour comparer, j'ai étendu le code de test hist.
Tout d'abord, je fais un test en utilisant une constante, ce qui me donne un certain timing incluant la boucle. Il s'agit d'un timing «nu», à l'exclusion de l'accès réel. Ensuite, je fais un test avec l'accès à la structure du sujet, cela me donne un timing, une boucle et un accès réel "overhead inclus".
La différence entre la synchronisation «nue» et la synchronisation «sans frais généraux» me donne une indication de la synchronisation «d'accès à la structure».
Mais quelle est la précision de ce timing? Pendant les tests, les fenêtres feront un certain temps de découpage pour shure. Je n'ai aucune information sur le découpage temporel, mais je suppose qu'il est uniformément réparti pendant le test et de l'ordre de dizaines de msec, ce qui signifie que la précision de la synchronisation doit être de l'ordre de +/- 100 msec environ. Une estimation un peu approximative? Quoi qu'il en soit, une source d'erreur de mesure systématique.
De plus, les tests ont été effectués en mode 'Debug' sans optimisation. Sinon, le compilateur pourrait changer le code de test réel.
Donc, j'obtiens deux résultats, un pour une constante, marqué «(c)», et un pour l'accès marqué «(n)» et la différence «dt» me dit combien de temps l'accès réel prend.
Et voici les résultats:
Avec de meilleures estimations sur les erreurs de synchronisation (comment supprimer l'erreur de mesure systématique due au découpage dans le temps?), On pourrait en dire plus sur les résultats.
Il semble que List / foreach a l'accès le plus rapide, mais la surcharge le tue.
La différence entre List / for et List / foreach est étrange. Peut-être qu'un encaissement est impliqué?
De plus, pour accéder à un tableau, peu importe si vous utilisez une
for
boucle ou uneforeach
boucle. Les résultats de synchronisation et leur précision rendent les résultats «comparables».Utiliser un dictionnaire est de loin le plus lent, je ne l'ai considéré que parce que sur le côté gauche (l'indexeur) j'ai une liste clairsemée d'entiers et non une plage comme celle utilisée dans ces tests.
Voici le code de test modifié.
la source
Dans quelques brefs tests, j'ai trouvé qu'une combinaison des deux était meilleure dans ce que j'appellerais des mathématiques raisonnablement intensives:
Type:
List<double[]>
Type:
List<List<double>>
Type:
double[rows * columns]
Exécution du code:
Je souhaite que nous ayons des classes matricielles accélérées par matériel de premier ordre comme l'équipe .NET l'a fait avec la
System.Numerics.Vectors
classe!C # pourrait être le meilleur langage ML avec un peu plus de travail dans ce domaine!
la source