Preservação de Encomendas em PLINQ
No PLINQ, o objetivo é maximizar o desempenho, mantendo a correção. Uma consulta deve ser executada o mais rápido possível, mas ainda assim produzir os resultados corretos. Em alguns casos, a correção requer que a ordem da sequência de origem seja preservada; no entanto, encomendar pode ser computacionalmente caro. Portanto, por padrão, PLINQ não preserva a ordem da sequência de origem. A este respeito, PLINQ se assemelha ao LINQ to SQL, mas é diferente do LINQ to Objects, que preserva a ordenação.
Para substituir o comportamento padrão, você pode ativar a preservação de ordem usando o AsOrdered operador na sequência de origem. Em seguida, você pode desativar a preservação de ordem posteriormente na consulta usando o AsUnordered método. Com ambos os métodos, a consulta é processada com base na heurística que determina se a consulta deve ser executada como paralela ou sequencial. Para obter mais informações, consulte Understanding Speedup in PLINQ.
O exemplo a seguir mostra uma consulta paralela não ordenada que filtra todos os elementos que correspondem a uma condição, sem tentar ordenar os resultados de forma alguma.
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)
Esta consulta não produz necessariamente as primeiras 1000 cidades na sequência de origem que cumprem a condição, mas sim algum conjunto de 1000 cidades que cumprem a condição. Os operadores de consulta PLINQ particionam a sequência de origem em várias subsequências que são processadas como tarefas simultâneas. Se a preservação da ordem não for especificada, os resultados de cada partição serão passados para a próxima etapa da consulta em uma ordem arbitrária. Além disso, uma partição pode produzir um subconjunto de seus resultados antes de continuar a processar os elementos restantes. A ordem resultante pode ser diferente de cada vez. Seu aplicativo não pode controlar isso porque depende de como o sistema operacional agenda os threads.
O exemplo a seguir substitui o comportamento padrão usando o AsOrdered operador na sequência de origem. Isso garante que o Take método retorna as primeiras 1000 cidades na sequência de origem que atendem à condição.
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)
No entanto, essa consulta provavelmente não é executada tão rápido quanto a versão não ordenada porque deve acompanhar a ordem original em todas as partições e, no momento da mesclagem, garantir que a ordem seja consistente. Portanto, recomendamos que você use AsOrdered somente quando for necessário e somente para as partes da consulta que o exigem. Quando a preservação do pedido não for mais necessária, use AsUnordered para desativá-lo. O exemplo a seguir consegue isso compondo duas consultas.
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
Observe que PLINQ preserva a ordenação de uma sequência produzida por operadores que impõem ordens para o resto da consulta. Por outras palavras, operadores como OrderBy e ThenBy são tratados como se fossem seguidos de uma chamada para AsOrdered.
Operadores de consulta e pedidos
Os operadores de consulta a seguir introduzem a preservação de ordem em todas as operações subsequentes em uma consulta, ou até AsUnordered que seja chamado:
Os seguintes operadores de consulta PLINQ podem, em alguns casos, exigir sequências de origem ordenadas para produzir resultados corretos:
Alguns operadores de consulta PLINQ se comportam de forma diferente, dependendo se sua sequência de origem é ordenada ou não ordenada. A tabela a seguir lista esses operadores.
Operador | Resultado quando a sequência de origem é ordenada | Resultado quando a sequência de origem não está ordenada |
---|---|---|
Aggregate | Produção não determinística para operações não associativas ou não comutativas | Produção não determinística para operações não associativas ou não comutativas |
All | Não aplicável | Não aplicável |
Any | Não aplicável | Não aplicável |
AsEnumerable | Não aplicável | Não aplicável |
Average | Produção não determinística para operações não associativas ou não comutativas | Produção não determinística para operações não associativas ou não comutativas |
Cast | Resultados ordenados | Resultados não ordenados |
Concat | Resultados ordenados | Resultados não ordenados |
Count | Não aplicável | Não aplicável |
DefaultIfEmpty | Não aplicável | Não aplicável |
Distinct | Resultados ordenados | Resultados não ordenados |
ElementAt | Retornar elemento especificado | Elemento arbitrário |
ElementAtOrDefault | Retornar elemento especificado | Elemento arbitrário |
Except | Resultados não ordenados | Resultados não ordenados |
First | Retornar elemento especificado | Elemento arbitrário |
FirstOrDefault | Retornar elemento especificado | Elemento arbitrário |
ForAll | Executa não deterministicamente em paralelo | Executa não deterministicamente em paralelo |
GroupBy | Resultados ordenados | Resultados não ordenados |
GroupJoin | Resultados ordenados | Resultados não ordenados |
Intersect | Resultados ordenados | Resultados não ordenados |
Join | Resultados ordenados | Resultados não ordenados |
Last | Retornar elemento especificado | Elemento arbitrário |
LastOrDefault | Retornar elemento especificado | Elemento arbitrário |
LongCount | Não aplicável | Não aplicável |
Min | Não aplicável | Não aplicável |
OrderBy | Reordena a sequência | Inicia nova secção encomendada |
OrderByDescending | Reordena a sequência | Inicia nova secção encomendada |
Range | Não aplicável (mesmo padrão que AsParallel ) | Não aplicável |
Repeat | Não aplicável (mesmo padrão que AsParallel) | Não aplicável |
Reverse | Reversos | Não faz nada |
Select | Resultados ordenados | Resultados não ordenados |
Select (indexado) | Resultados ordenados | Resultados não ordenados. |
SelectMany | Resultados ordenados. | Resultados não ordenados |
SelectMany (indexado) | Resultados ordenados. | Resultados não ordenados. |
SequenceEqual | Comparação ordenada | Comparação não ordenada |
Single | Não aplicável | Não aplicável |
SingleOrDefault | Não aplicável | Não aplicável |
Skip | Ignora os primeiros n elementos | Ignora quaisquer n elementos |
SkipWhile | Resultados ordenados. | Não determinística. Executa SkipWhile na ordem arbitrária atual |
Sum | Produção não determinística para operações não associativas ou não comutativas | Produção não determinística para operações não associativas ou não comutativas |
Take | Leva os primeiros n elementos |
Leva todos os n elementos |
TakeWhile | Resultados ordenados | Não determinística. Executa TakeWhile na ordem arbitrária atual |
ThenBy | Suplementos OrderBy |
Suplementos OrderBy |
ThenByDescending | Suplementos OrderBy |
Suplementos OrderBy |
ToArray | Resultados ordenados | Resultados não ordenados |
ToDictionary | Não aplicável | Não aplicável |
ToList | Resultados ordenados | Resultados não ordenados |
ToLookup | Resultados ordenados | Resultados não ordenados |
Union | Resultados ordenados | Resultados não ordenados |
Where | Resultados ordenados | Resultados não ordenados |
Where (indexado) | Resultados ordenados | Resultados não ordenados |
Zip | Resultados ordenados | Resultados não ordenados |
Os resultados não ordenados não são ativamente embaralhados; eles simplesmente não têm nenhuma lógica de ordenação especial aplicada a eles. Em alguns casos, uma consulta não ordenada pode manter a ordem da sequência de origem. Para consultas que usam o operador Select indexado, o PLINQ garante que os elementos de saída sairão na ordem crescente dos índices, mas não garante quais índices serão atribuídos a quais elementos.