39 votes

Performances "Nested foreach" vs "lambda / linq query" (LINQ-to-Objects)

Du point de vue des performances, que devriez-vous utiliser "requêtes imbriquées" ou "requêtes lambda / linq"?

59voto

Jon Skeet Points 692016

Écrire le plus clair de code que vous pouvez, puis de référence et le profil à découvrir les éventuels problèmes de performances. Si vous n' avez des problèmes de performances, vous pouvez expérimenter avec différents code pour déterminer si elle est plus rapide ou pas (pour la mesure de tout le temps avec des données réalistes que possible) et ensuite prendre une décision quant à savoir si l'amélioration de la performance est digne de la lisibilité frappé.

Un direct, foreach approche permettra d' être plus rapide que LINQ dans de nombreux cas. Par exemple, pensez à:

var query = from element in list
            where element.X > 2
            where element.Y < 2
            select element.X + element.Y;

foreach (var value in query)
{
    Console.WriteLine(value);
}

Il y a maintenant deux where les clauses et un select de la clause, de sorte que toute éventuelle de l'élément a à passer par trois des itérateurs. (Évidemment, les deux clauses where pourraient être combinés dans ce cas, mais je suis en train de faire un point général.)

Maintenant, comparez cela avec le code directement:

foreach (var element in list)
{
    if (element.X > 2 && element.Y < 2)
    {
        Console.WriteLine(element.X + element.Y);
    }
}

Qui va courir plus vite, car il a moins de cerceaux à courir à travers. Les Chances sont que la sortie de la console sera un nain de l'itérateur de coûts, et j'aurais préférer la requête LINQ.

EDIT: Pour répondre au sujet de "foreach imbriquée" boucles... généralement ceux qui sont représentés avec des SelectMany ou un second from clause de:

var query = from item in firstSequence
            from nestedItem in item.NestedItems
            select item.BaseCount + nestedItem.NestedCount;

Ici, nous ne sommes qu'à l'ajout d'un unique supplémentaire pour l'itérateur, parce que nous avions déjà être en utilisant un supplément de itérateur par élément dans la première séquence, en raison de la imbriquée foreach boucle. Il y a encore un peu de surcharge, y compris les frais généraux de faire de la projection dans un délégué à la place de "inline" (quelque chose que je n'ai pas mentionné avant) mais il ne sera pas très différente de la nested-foreach performance.

Ce n'est pas à dire que vous ne pouvez pas se tirer dans le pied avec LINQ, bien sûr. Vous pouvez écrire stupendously requêtes inefficace si vous ne vous engagez pas votre cerveau de première, mais c'est loin d'être unique à LINQ...

23voto

David B Points 53123

Si tu fais

 foreach(Customer c in Customer)
{
  foreach(Order o in Orders)
  {
    //do something with c and o
  }
}
 

Vous effectuerez des itérations Customer.Count * Order.Count


Si tu fais

 var query =
  from c in Customer
  join o in Orders on c.CustomerID equals o.CustomerID
  select new {c, o}

foreach(var x in query)
{
  //do something with x.c and x.o
}
 

Vous effectuerez des itérations Customer.Count + Order.Count, car Enumerable.Join est implémenté en tant que HashJoin.

12voto

Marc Gravell Points 482669

Il est plus complexe que. En fin de compte, beaucoup de LINQ-to-Objets (les coulisses) un foreach boucle, mais avec l'avantage supplémentaire d'un peu d'abstraction / itérateur blocs / etc. Cependant, à moins que vous faire des choses très différentes dans les deux versions (foreach vs LINQ), ils doivent tous les deux être en O(N).

La vraie question est: est-il une meilleure façon de la rédaction de votre spécifique algorithme qui signifie qu' foreach serait inefficace? Et peut LINQ faire pour vous?

Par exemple, LINQ rend facile de hachage / groupe / tri des données.

2voto

Drithyin Points 48

Il a été dit avant, mais cela mérite d'être répété.

Les développeurs ne sait jamais où le goulot d'étranglement des performances est, jusqu'à épuisement des tests de performance.

La même chose est vraie pour la comparaison de la technique Une technique B. Sauf si il y a une énorme différence, alors vous avez juste à le tester. Il pourrait être évident si vous avez un O(n) vs O(n^x) scénario, mais depuis le LINQ étoffe est surtout compilateur de la sorcellerie, il mérite un profilage.

En outre, à moins que votre projet est en production et que vous avez présenté le code et a constaté que cette boucle est de ralentir votre exécution, laissez le comme ça, quelle que soit votre préférence pour la lisibilité et la maintenance. L'optimisation prématurée est le diable.

2voto

Denis Troller Points 5774

Un grand avantage est que l'utilisation de requêtes Linq-To-Objects vous permet de passer facilement la requête à PLinq et de laisser le système effectuer automatiquement l'opération sur le nombre correct de threads du système actuel.

Si vous utilisez cette technique sur de grands ensembles de données, vous gagnerez facilement sans grand problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X