2010-04-27 12 views
0

Je me demandais comment pourrais-je améliorer les performances du code ci-dessous:Performance d'un obtenir des éléments uniques/groupe par opération sur un IEnumerable <T>

public class MyObject 
{ 
    public int Year { get; set; } 
} 

//In my case I have 30000 
IEnumerable<MyObject> data = MethodThatReturnsManyMyObjects(); 

var groupedByYear = data.GroupBy(x => x.Year); 

//Here is the where it takes around 5 seconds 
foreach (var group in groupedByYear) 
    //do something here. 

L'idée est d'obtenir un ensemble d'objets avec année uniques valeurs. Dans mon scénario, il n'y a que 6 ans inclus dans les 30000 éléments de la liste, donc la boucle foreach sera exécutée 6 fois seulement. Nous avons donc beaucoup d'éléments à regrouper dans quelques groupes. L'utilisation de .Distinct() avec un IEqualityComparer explicite serait une alternative, mais je pense que cela ne fera aucune différence.

Je peux comprendre si 30000 articles est trop et que je devrais être heureux avec les 5 secondes que je reçois, mais je me demandais si les performances ci-dessus peuvent être améliorées.

Merci.

EDIT: Les réponses ci-dessous m'ont fait creuser un peu plus profond seulement pour réaliser que les 5 secondes que je reçois seulement heppens quand les données sont chargées dans la mémoire du DB. Le retard a été déguisé dans la boucle foreach car l'exécution différée de IEnumerable l'a retardé jusqu'à ce point me confondant pour supposer que probablement le GroupBy() pourrait être refactorisé à quelque chose de plus performant.

La question subsiste cependant, est la commande GroupBy() le moyen optimal pour atteindre les meilleures performances dans de tels cas?

+1

curiosité, où est MethodThatReturnsManyMyObjects obtenir les objets à partir? –

+0

Avez-vous seulement besoin des 6 valeurs uniques de l'année, ou avez-vous besoin des objets MyObject correspondant à chacune de ces 6 années? –

+0

@Ian P: Les données sont toutes en mémoire. Ils sont obtenus à partir de la base de données et conservés en mémoire avant d'être fournis à IEnumerable ou proviennent d'un objet mémoire mis en cache. – tolism7

Répondre

2

Cela ne devrait certainement pas prendre autant de temps. Est-ce que cela fonctionne sous le débogueur, ou pas? Des exceptions sont-elles levées? La propriété Year effectue-t-elle des calculs dans la vie réelle? Il devrait l'exécuter presque instantanément, pour être honnête.

Avez-vous un programme court mais complet qui démontre que cela prend du temps? (Si ce n'est pas le cas, j'essaierai d'en trouver un moi-même pour obtenir des timings d'échantillon.)

Notez que si MethodThatReturnsManyMyObjects utilise une exécution différée pour l'itérateur, cela pourrait être le coupable - combien de temps cela prend-il si vous appelez data.ToList() par exemple?

+0

@Jon Merci pour la perspicacité. J'essaye actuellement d'obtenir de meilleurs temps de benchamrk à fournir. Je vais avoir quelque chose à montrer dans un peu. – tolism7

+0

Je soupçonne que l'évaluation différée est le problème. Dans un exemple simple que j'ai fait sur la base des critères fournis, il est presque instantané avec 350 000 échantillons. –

+0

@Ian & Jon: Je viens de me féliciter de la performance de mon auto. La méthode qui récupère les données peut les récupérer dans la base de données OU dans un cache en mémoire. Chaque fois que le cache en mémoire fournit des données, le processus prend 20 millisecondes, mais lorsque les données proviennent de la BD (en utilisant NHibernate) et que je m'assure qu'elles sont chargées en mémoire et que la BD n'est pas touchée plus d'une fois, 6 secondes J'ai besoin de creuser plus profond pour savoir pourquoi. – tolism7

1

Je suis curieux de savoir: votre MethodThatReturnsManyMyObjects fournit-il une évaluation paresseuse (c'est-à-dire, en utilisant le mot-clé yield)? Si oui, que pourrait être votre coupable, plutôt que l'appel à GroupBy:

// if MethodThatReturnsManyMyObjects uses yield, then 
// it won't be executed until enumeration 
IEnumerable<MyObject> data = MethodThatReturnsManyMyObjects(); 

// still not executed 
var groupedByYear = data.GroupBy(x => x.Year); 

// finally executed here 
foreach (var group in groupedByYear) 
    // ...