Du point de vue des performances, que devriez-vous utiliser "requêtes imbriquées" ou "requêtes lambda / linq"?
Réponses
Trop de publicités?É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...
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.
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.
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.
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.