Conservation de l'ordre en PLINQ
Dans PLINQ, l’objectif est d’augmenter les performances tout en préservant l’exactitude. Une requête doit s’exécuter aussi rapidement que possible, mais toujours générer des résultats corrects. Dans certains cas, l’exactitude requiert que l’ordre de la séquence source soit conservé ; toutefois, le classement peut coûter cher en calcul. Par conséquent, par défaut, PLINQ ne conserve pas l’ordre de la séquence source. À cet égard, PLINQ ressemble à LINQ to SQL, mais ne ressemble pas à LINQ to Objects, qui préserve l'ordre.
Pour remplacer le comportement par défaut, vous pouvez activer la conservation de l’ordre à l’aide de l’opérateur AsOrdered sur la séquence source. Vous pouvez désactiver la préservation de l’ordre plus loin dans la requête à l’aide de la méthode AsUnordered. Ces deux méthodes permettent de traiter la requête en fonction des paramètres heuristiques, qui déterminent s’il faut exécuter la requête en parallèle ou de manière séquentielle. Pour plus d’informations, consultez Fonctionnement de l’accélération dans PLINQ.
L’exemple suivant montre une requête parallèle non classée qui filtre tous les éléments qui correspondent à une condition, sans essayer de classer les résultats de quelque manière que ce soit.
var cityQuery =
(from city in cities.AsParallel()
where city.Population > 10000
select city).Take(1000);
Dim cityQuery = From city In cities.AsParallel()
Where city.Population > 10000
Take (1000)
Cette requête ne produit pas nécessairement les 1 000 premières villes de la séquence source qui remplissent la condition, mais plutôt un ensemble de 1 000 villes qui remplissent la condition. Les opérateurs de requête PLINQ partitionnent la séquence source en plusieurs sous-séquences qui sont traitées comme des tâches simultanées. Si la conservation de l’ordre n’est pas spécifiée, les résultats de chaque partition sont remis à l’étape suivante de la requête dans un ordre arbitraire. Par ailleurs, une partition peut donner un sous-ensemble de ses résultats avant de continuer à traiter les éléments restants. L’ordre résultant peut être différent à chaque fois. Ceci ne peut pas être contrôlé par votre application, car cela dépend de la manière dont le système d’exploitation planifie les threads.
L’exemple suivant remplace le comportement par défaut à l’aide de l’opérateur AsOrdered sur la séquence source. Cela garantit que la méthode Take retourne les 1 000 premières villes de la séquence source qui remplissent la condition.
var orderedCities =
(from city in cities.AsParallel().AsOrdered()
where city.Population > 10000
select city).Take(1000);
Dim orderedCities = From city In cities.AsParallel().AsOrdered()
Where city.Population > 10000
Take (1000)
Toutefois, cette requête ne s’exécute probablement pas aussi rapidement que la version non classée, car elle doit surveiller l’ordre d’origine dans toutes les partitions et s’assurer que le classement est cohérent au moment de la fusion. Par conséquent, nous vous recommandons de n’utiliser AsOrdered que si c’est nécessaire, et uniquement pour les parties de la requête qui l’exigent. Lorsque la conservation de l’ordre n’est plus nécessaire, utilisez AsUnordered pour la désactiver. L’exemple suivant obtient ce résultat en composant deux requêtes.
var orderedCities2 =
(from city in cities.AsParallel().AsOrdered()
where city.Population > 10000
select city).Take(1000);
var finalResult =
from city in orderedCities2.AsUnordered()
join p in people.AsParallel()
on city.Name equals p.CityName into details
from c in details
select new
{
city.Name,
Pop = city.Population,
c.Mayor
};
foreach (var city in finalResult) { /*...*/ }
Dim orderedCities2 = From city In cities.AsParallel().AsOrdered()
Where city.Population > 10000
Select city
Take (1000)
Dim finalResult = From city In orderedCities2.AsUnordered()
Join p In people.AsParallel() On city.Name Equals p.CityName
Select New With {.Name = city.Name, .Pop = city.Population, .Mayor = city.Mayor}
For Each city In finalResult
Console.WriteLine(city.Name & ":" & city.Pop & ":" & city.Mayor)
Next
Notez que PLINQ conserve le classement d’une séquence produite par des opérateurs imposant un ordre pour le reste de la requête. En d’autres termes, les opérateurs tels que OrderBy et ThenBy sont traités comme s’ils étaient suivis d’un appel à AsOrdered.
Opérateurs de requête et de classement
Les opérateurs de requête suivants présentent la conservation de l’ordre dans toutes les opérations suivantes d’une requête, ou jusqu'à ce que AsUnordered soit appelée :
dans certains cas, les opérateurs de requête PLINQ suivants peuvent exiger des séquences source classées pour produire des résultats corrects :
certains opérateurs de requête PLINQ se comportent différemment, selon que leur séquence source est classée ou non classée. Le tableau suivant répertorie ces opérateurs.
Opérateur | Résultat lorsque la séquence source est classée | Résultat de la séquence source n’est pas classée |
---|---|---|
Aggregate | Sortie non déterministe pour les opérations non associatives ou non commutatives | Sortie non déterministe pour les opérations non associatives ou non commutatives |
All | Non applicable | Non applicable |
Any | Non applicable | Non applicable |
AsEnumerable | Non applicable | Non applicable |
Average | Sortie non déterministe pour les opérations non associatives ou non commutatives | Sortie non déterministe pour les opérations non associatives ou non commutatives |
Cast | Résultats classés | Résultats non classés |
Concat | Résultats classés | Résultats non classés |
Count | Non applicable | Non applicable |
DefaultIfEmpty | Non applicable | Non applicable |
Distinct | Résultats classés | Résultats non classés |
ElementAt | Retourner un élément spécifié | Élément arbitraire |
ElementAtOrDefault | Retourner un élément spécifié | Élément arbitraire |
Except | Résultats non classés | Résultats non classés |
First | Retourner un élément spécifié | Élément arbitraire |
FirstOrDefault | Retourner un élément spécifié | Élément arbitraire |
ForAll | Exécute de façon non déterministe en parallèle | Exécute de façon non déterministe en parallèle |
GroupBy | Résultats classés | Résultats non classés |
GroupJoin | Résultats classés | Résultats non classés |
Intersect | Résultats classés | Résultats non classés |
Join | Résultats classés | Résultats non classés |
Last | Retourner un élément spécifié | Élément arbitraire |
LastOrDefault | Retourner un élément spécifié | Élément arbitraire |
LongCount | Non applicable | Non applicable |
Min | Non applicable | Non applicable |
OrderBy | Réorganise la séquence | Démarre une nouvelle section classée |
OrderByDescending | Réorganise la séquence | Démarre une nouvelle section classée |
Range | Non applicable (même valeur par défaut que AsParallel) | Non applicable |
Repeat | Non applicable (même valeur par défaut que AsParallel) | Non applicable |
Reverse | Inverse | Sans effet |
Select | Résultats classés | Résultats non classés |
Select (indexé) | Résultats classés | Résultats non classés. |
SelectMany | Résultats classés. | Résultats non classés |
SelectMany (indexé) | Résultats classés. | Résultats non classés. |
SequenceEqual | Comparaison classée | Comparaison non classée |
Single | Non applicable | Non applicable |
SingleOrDefault | Non applicable | Non applicable |
Skip | Ignore les n premiers éléments | Ignore les n éléments |
SkipWhile | Résultats classés. | Non déterministe. Exécute SkipWhile dans l’ordre arbitraire actuel |
Sum | Sortie non déterministe pour les opérations non associatives ou non commutatives | Sortie non déterministe pour les opérations non associatives ou non commutatives |
Take | Prend les premiers éléments n |
Prend tous les éléments n |
TakeWhile | Résultats classés | Non déterministe. Exécute TakeWhile dans l’ordre arbitraire actuel |
ThenBy | Complète OrderBy |
Complète OrderBy |
ThenByDescending | Complète OrderBy |
Complète OrderBy |
ToArray | Résultats classés | Résultats non classés |
ToDictionary | Non applicable | Non applicable |
ToList | Résultats classés | Résultats non classés |
ToLookup | Résultats classés | Résultats non classés |
Union | Résultats classés | Résultats non classés |
Where | Résultats classés | Résultats non classés |
Where (indexé) | Résultats classés | Résultats non classés |
Zip | Résultats classés | Résultats non classés |
Les résultats non classés ne sont pas mélangés de manière active ; aucune logique de classement spéciale ne leur est appliquée. Dans certains cas, une requête non classée peut conserver le classement de la séquence source. Pour les requêtes utilisant l’opérateur Select indexé, PLINQ garantit que les éléments de sortie arriveront dans l’ordre des indices croissant, mais n’offre aucune garantie quant aux index qui seront affectés aux éléments respectifs.