Conservation de l'ordre en PLINQ
En PLINQ, l'objectif est de maximiser les performances tout en maintenant l'exactitude. Une requête doit s'exécuter aussi rapidement que possible mais doit produire des résultats corrects. Dans certains cas, l'exactitude requiert que l'ordre de la séquence source soit conservé ; toutefois, le classement sollicite fortement les ressources informatiques. Par conséquent, PLINQ ne conserve pas par défaut l'ordre de la séquence source. À cet égard, PLINQ est similaire à LINQ to SQL, mais contrairement à LINQ to Objects, il ne conserve pas le classement.
Pour substituer le comportement par défaut, vous pouvez activer la conservation de l'ordre en utilisant l'opérateur AsOrdered sur la séquence source. Vous pouvez désactiver ultérieurement la conservation de l'ordre dans la requête à l'aide de la méthode AsUnordered<TSource>. Avec les deux méthodes, la requête est traitée en fonction des paramètres heuristiques qui déterminent si la requête doit être exécutée de manière parallèle ou séquentielle. Pour plus d'informations, consultez Fonctionnement de l'accélération dans PLINQ.
L'exemple suivant affiche 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.
Dim cityQuery = From city In cities.AsParallel()
Where City.Population > 10000
Take (1000)
var cityQuery = (from city in cities.AsParallel()
where city.Population > 10000
select city)
.Take(1000);
Cette requête ne renvoie pas nécessairement les 1000 premières villes de la séquence source qui remplissent la condition, mais plutôt un ensemble de 1000 villes qui remplissent la condition. Les opérateurs de requête PLINQ partitionnent la séquence source en plusieurs sous-séquences 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 donnés à l'étape suivante de la requête dans un ordre arbitraire. De plus, une partition peut transmettre un sous-ensemble de ses résultats avant de continuer le traitement des éléments restants. L'ordre résultant peut être différent à chaque fois. Votre application ne peut pas contrôler cela parce cela dépend de la manière dont le système d'exploitation planifie les threads.
L'exemple suivant substitue le comportement par défaut en utilisant l'opérateur AsOrdered sur la séquence source. Cela vérifie que la méthode Take<TSource> retourne les 10 premières villes de la séquence source qui remplissent la condition.
Dim orderedCities = From city In cities.AsParallel().AsOrdered()
Where City.Population > 10000
Take (1000)
var orderedCities = (from city in cities.AsParallel().AsOrdered()
where city.Population > 10000
select city)
.Take(1000);
Toutefois, cette requête ne s'exécute pas aussi rapidement que la version non classée probablement parce qu'elle doit effectuer le suivi du classement d'origine dans toutes les partitions et vérifier que le classement est cohérent au moment de la fusion. Par conséquent, il est recommandé d'utiliser AsOrdered uniquement lorsque cela est obligatoire et uniquement pour les parties de la requête qui le nécessitent. Lorsque la conservation de l'ordre n'est plus obligatoire, utilisez AsUnordered<TSource> pour la désactiver. L'exemple suivant effectue ceci en composant deux requêtes.
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
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 { Name = city.Name, Pop = city.Population, Mayor = c.Mayor };
foreach (var city in finalResult) { /*...*/ }
Notez que PLINQ conserve le classement d'une séquence produit par les opérateurs d'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 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<TSource> soit appelé :
Les opérateurs de requête PLINQ suivants peuvent nécessiter dans certains cas que les séquences sources classées produisent 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. Le tableau suivant répertorie ces opérateurs.
Opérateur |
Résultat si la séquence source est classée |
Résultat si la séquence source n'est pas classée |
---|---|---|
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 |
|
Non applicable |
Non applicable |
|
Non applicable |
Non applicable |
|
Non applicable |
Non applicable |
|
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 |
|
Résultats classés |
Résultats non classés |
|
Résultats classés |
Résultats non classés |
|
Non applicable |
Non applicable |
|
Non applicable |
Non applicable |
|
Résultats classés |
Résultats non classés |
|
Retourne l'élément spécifié |
Élément arbitraire |
|
Retourne l'élément spécifié |
Élément arbitraire |
|
Résultats non classés |
Résultats non classés |
|
Retourne l'élément spécifié |
Élément arbitraire |
|
Retourne l'élément spécifié |
Élément arbitraire |
|
Exécute en parallèle de manière non déterministe |
Exécute en parallèle de manière non déterministe |
|
Résultats classés |
Résultats non classés |
|
Résultats classés |
Résultats non classés |
|
Résultats classés |
Résultats non classés |
|
Résultats classés |
Résultats non classés |
|
Retourne l'élément spécifié |
Élément arbitraire |
|
Retourne l'élément spécifié |
Élément arbitraire |
|
Non applicable |
Non applicable |
|
Non applicable |
Non applicable |
|
Réorganise la séquence |
Démarre la nouvelle section classée |
|
Réorganise la séquence |
Démarre la nouvelle section classée |
|
Non applicable (même valeur par défaut que AsParallel) |
Non applicable |
|
Non applicable (même valeur par défaut que AsParallel) |
Non applicable |
|
Inverse |
Sans effet |
|
Résultats classés |
Résultats non classés |
|
Select (indexé) |
Résultats classés |
Résultats non classés |
Résultats classés |
Résultats non classés |
|
SelectMany (indexé) |
Résultats classés |
Résultats non classés |
Comparaison classée |
Comparaison non classée |
|
Non applicable |
Non applicable |
|
Non applicable |
Non applicable |
|
Ignore en premier les éléments n |
Ignore tous les éléments n |
|
Résultats classés |
Non déterministe Exécute SkipWhile sur l'ordre arbitraire actuel |
|
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 |
|
Prend en premier les éléments n |
Prend tous les éléments n |
|
Résultats classés |
Non déterministe Exécute TakeWhile sur l'ordre arbitraire actuel |
|
Complète OrderBy |
Complète OrderBy |
|
Complète OrderBy |
Complète OrderBy |
|
Résultats classés |
Résultats non classés |
|
Non applicable |
Non applicable |
|
Résultats classés |
Résultats non classés |
|
Résultats classés |
Résultats non classés |
|
Résultats classés |
Résultats non classés |
|
Résultats classés |
Résultats non classés |
|
Where (indexé) |
Résultats classés |
Résultats non classés |
Résultats classés |
Résultats non classés |
Les résultats non classés ne sont pas lus activement de manière aléatoire ; tout simplement, aucune logique de tri 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 qui utilisent l'opérateur Select indexé, PLINQ garantit que les résultats seront générés dans l'ordre croissant des index, mais n'offre aucune garantie sur la nature des index affectés aux résultats.