Performance avec Linq to object: streamée ou non ?

Rappelons un point important qu'il est toujours bon d'avoir en tête lorsque l'on fait du Linq to Object.
Une majorité des opérations de Linq déroulent l'énumération source et exploitent chaque élément un par un pour effectuer leur traitement. Aucune collection intermédiaire interne n'est utilisée. Nous appelons ces opérations "streamed". Comme le nom l'indique, ces opération travaillent directement sur le flux de données sans avoir besoin d'une connaissance globale des données.

Prenons des cas simples:

.Where() est entièrement autonome sur l'élément en cours. Aucune autre donnée du flux ne lui est nécessaire pour savoir si oui ou non, un élément doit être filtré ou pas.

De même, Select, Disctinct, Any, etc, sont des méthodes "streamed".

Par opposition,

.Count() de toute évidence doit balayer l'intégralité du flux pour renvoyer son résultat.

C'est également vrai pour OrderBy, GroupBy, All, etc.

Il faut être conscient de l'impact de ces méthodes sur la performance.

Voici un exemple simple:

 var q =
    from n in names
    where n.StartsWith("A")
    select n;

foreach (var n in q)
{
    //traitement
}

int count = q.Count();

le code suivant va balayer deux fois de suite l'intégralité des données renvoyées par 'q'. Une fois de manière explicite dans le foreach, puis une fois dans le .Count().

Dans ce cas précis nous savons pertinemment que nous allons parcourir l'ensemble des éléments dans le foreach. Afin de ne pas parcourir l'énumération une seconde fois, il est préférable de:

- soit ajouter un compteur dans le foreach (count++).

- soit, de projeter l'ensemble des données dans un tableau si d'après votre contexte sa taille est raisonnable.

 var q =
    from n in names
    where n.StartsWith("A")
    select n;

var a = q.ToArray();

foreach (var n in a)
{
    //traitement
}

int count = a.Lenght;

Si la source de vos données est une structure mémoire (List<>, Array, etc) de taille moyenne, l'impact au niveau de la performance restera relativement faible mais toujours intéressant. Par contre si votre source est un flux, imaginons un reader sur un fichier texte sous forme d'énumérateur ou pire encore un reader sur un flux réseau type socket par exemple, il faut bien être conscient qu'une requête Linq avec un simple OrderBy ne renverra le premier élément qu'après avoir lu l'intégralité du flux...