LINQ méthode pour ajouter des éléments à un dictionnaire
J'essaie d'en apprendre un peu plus sur LINQ par la mise en œuvre de Peter Norvig de correcteur orthographique en C#.
La première partie consiste à prendre une grande fichier de mots (environ 1 million) et de le mettre dans un dictionnaire où l' key
est la parole et la value
est le nombre d'occurrences.
Je serais normalement ce faire comme suit:
foreach (var word in allWords)
{
if (wordCount.ContainsKey(word))
wordCount[word]++;
else
wordCount.Add(word, 1);
}
Où allWords
est un IEnumerable<string>
Dans LINQ, je suis actuellement en train de faire comme ceci:
var wordCountLINQ = (from word in allWordsLINQ
group word by word
into groups
select groups).ToDictionary(g => g.Key, g => g.Count());
Je compare les 2 dictionnaires en regardant toutes les <key, value>
et qu'ils sont identiques, de sorte qu'ils produisent les mêmes résultats.
La foreach
boucle prend 3.82 secs et la requête LINQ prend 4.49 secs
Je suis le calendrier à l'aide de la fonction Chronomètre de la classe et je suis en cours d'exécution en mode RELEASE. Je ne pense pas que le rendement est mauvais, je me demandais juste si il y avait une raison pour la différence.
Je fais la requête LINQ dans un moyen inefficace ou ai-je raté quelque chose?
Mise à jour: voici le plein de référence exemple de code:
public static void TestCode()
{
//File can be downloaded from http://norvig.com/big.txt and consists of about a million words.
const string fileName = @"path_to_file";
var allWords = from Match m in Regex.Matches(File.ReadAllText(fileName).ToLower(), "[a-z]+", RegexOptions.Compiled)
select m.Value;
var wordCount = new Dictionary<string, int>();
var timer = new Stopwatch();
timer.Start();
foreach (var word in allWords)
{
if (wordCount.ContainsKey(word))
wordCount[word]++;
else
wordCount.Add(word, 1);
}
timer.Stop();
Console.WriteLine("foreach loop took {0:0.00} ms ({1:0.00} secs)\n",
timer.ElapsedMilliseconds, timer.ElapsedMilliseconds / 1000.0);
//Make LINQ use a different Enumerable (with the exactly the same values),
//if you don't it suddenly becomes way faster, which I assmume is a caching thing??
var allWordsLINQ = from Match m in Regex.Matches(File.ReadAllText(fileName).ToLower(), "[a-z]+", RegexOptions.Compiled)
select m.Value;
timer.Reset();
timer.Start();
var wordCountLINQ = (from word in allWordsLINQ
group word by word
into groups
select groups).ToDictionary(g => g.Key, g => g.Count());
timer.Stop();
Console.WriteLine("LINQ took {0:0.00} ms ({1:0.00} secs)\n",
timer.ElapsedMilliseconds, timer.ElapsedMilliseconds / 1000.0);
}
J'ai juste ajouté que dans la question pour vous.
OriginalL'auteur Matt Warren | 2010-01-22
Vous devez vous connecter pour publier un commentaire.
L'une des raisons du LINQ version est plus lente, c'est parce qu'au lieu d'un dictionnaire à l'autre, les deux dictionnaires sont créés:
(interne) de la part du groupe par l'opérateur; le groupe par la aussi les magasins de chaque mot. Vous pouvez vérifier cela en regardant un ToArray() plutôt qu'un Count(). C'est beaucoup de frais généraux, vous n'avez pas besoin dans votre cas.
La ToDictionary méthode est fondamentalement un foreach sur le réel d'une requête LINQ, où les résultats de la requête sont ajoutés à un nouveau dictionnaire. Selon le nombre de mots uniques, cela peut prendre un certain temps.
Une autre raison que la requête LINQ est un peu plus lent, c'est parce que LINQ s'appuie sur les expressions lambda (le délégué à Dathan de réponse), et d'appeler un délégué ajoute une petite quantité de frais généraux par rapport au code en ligne.
Edit: Noter que pour certains LINQ scénarios (comme LINQ to SQL, mais pas en mémoire LINQ comme ici), la réécriture de la requête produit une plus optimisé plan:
Noter, cependant, que cela ne vous donne pas un Dictionnaire, mais plutôt d'une séquence de mots et de leur compte. Vous pouvez le transformer en un Dictionnaire avec
Autant que je sache, pas de 3.5 ou 4.0, pas de. Pour que cela fonctionne, le ToDictionary et Grouper les opérateurs se doivent de coopérer lorsque vous êtes seulement à l'agrégation des données. Pour mémoire LINQ qui n'arrivera pas.
OriginalL'auteur Ruben
Quand j'construire votre deuxième exemple, puis l'ouvrir dans du Réflecteur, le démontage de vue, je reçois le texte suivant:
Probablement, il prend plus de temps juste parce qu'il y a plus d'appels de fonction qui se passe, et au cours de millions d'itérations qui s'ajoute aux autres.
Pas vraiment, que je sache. Peut-être par une autre expression de sélection? Je suis hors de mon domaine d'expérience dès que le groupe est impliqué dans l'expression.
OriginalL'auteur Dathan
Complètement abuser de LINQ j'ai été en mesure d'obtenir qu'il soit autour de la même chose et souvent légèrement plus rapide que la boucle foreach, même avec un délégué appel:
Même de changer le
foreach
d'utiliser un ensemble similaire de l'expression n'ai pas le rendre plus rapide.OriginalL'auteur NetMage
Vous pouvez résoudre votre problème en utilisant la lambda expression:
Je ne me posais pas de question ici, c'est une solution pour la question ci-dessus.
Alors, quelle partie de la question, est-il la réponse?
Qui a posé la question ci-dessus voulu savoir comment faire pour ajouter des éléments à un dictionnaire, c'est la solution pour l'ajout de l'élément de dictionnaire à l'aide de l'Unité de travail et d'un Dépôt de modèle
besoin de critiquer les réponses je souhaite que c'était vrai. Il y a révision complète des systèmes à Débordement de Pile pour le faire, sinon, serait devenu un tas d'ordures en un rien de temps. C'est un répondeur de la responsabilité de chercher plus loin que la question du titre. Cette "réponse" convertit essentiellement un morceau de code qui fonctionne à partir de la question dans la syntaxe de méthode. L'OP n'a pas demandé que, si la réponse est rien, mais inutile d'encombrer que du côté des pistes pour les futurs lecteurs. L'écrivain doit le supprimer, mais il ne veut pas car il va perdre 8 réputation de point.
OriginalL'auteur Muhammad Masud