Сохранение порядка в PLINQOrder Preservation in PLINQ

PLINQ предназначен для повышения производительности при сохранении правильности вычислений.In PLINQ, the goal is to maximize performance while maintaining correctness. Запрос должен выполняться как можно быстрее, но всегда давать правильные результаты.A query should run as fast as possible but still produce the correct results. В некоторых случаях для правильных расчетов нужно соблюдать порядок исходной последовательности, но упорядочение может требовать больших вычислительных ресурсов.In some cases, correctness requires the order of the source sequence to be preserved; however, ordering can be computationally expensive. Поэтому по умолчанию PLINQ не сохраняет порядок исходной последовательности.Therefore, by default, PLINQ does not preserve the order of the source sequence. В этом PLINQ похож на LINQ to SQLLINQ to SQL, но отличается от LINQ to Objects, который строго сохраняет порядок.In this regard, PLINQ resembles LINQ to SQLLINQ to SQL, but is unlike LINQ to Objects, which does preserve ordering.

Чтобы переопределить поведение по умолчанию, можно принудительно затребовать сохранение порядка, указав оператор AsOrdered в исходной последовательности.To override the default behavior, you can turn on order-preservation by using the AsOrdered operator on the source sequence. Сохранение порядка в запросе в последующих частях запроса можно отключить с помощью метода AsUnordered.You can then turn off order preservation later in the query by using the AsUnordered method. В обоих случаях запрос обрабатывается с учетом эвристического анализа, который выбирает параллельный или последовательный режим обработки.With both methods, the query is processed based on the heuristics that determine whether to execute the query as parallel or as sequential. Дополнительные сведения см. в разделе Общее представление об ускорении выполнения в PLINQ.For more information, see Understanding Speedup in PLINQ.

Следующий пример демонстрирует неупорядоченный параллельный запрос, который отбирает все соответствующие условию элементы, не пытаясь упорядочивать результаты.The following example shows an unordered parallel query that filters for all the elements that match a condition, without trying to order the results in any way.

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)

Этот запрос не всегда возвращает первые 1000 городов из исходной последовательности, которые отвечают условию, а просто некоторый набор из 1000 подходящих городов.This query does not necessarily produce the first 1000 cities in the source sequence that meet the condition, but rather some set of 1000 cities that meet the condition. Операторы запроса PLINQ разделяют исходную последовательность на несколько подпоследовательностей и обрабатывают их как одновременные задачи.PLINQ query operators partition the source sequence into multiple subsequences that are processed as concurrent tasks. Если сохранение порядка не требуется, результаты из каждого сегмента передаются на следующий этап обработки в произвольном порядке.If order preservation is not specified, the results from each partition are handed off to the next stage of the query in an arbitrary order. Кроме того, поток обработки сегмента может возвращать подмножество результатов раньше, чем продолжит обработку следующих элементов.Also, a partition may yield a subset of its results before it continues to process the remaining elements. Порядок полученных результатов будет разным при каждом выполнении.The resulting order may be different every time. Приложение не может контролировать результат, поскольку он зависит от распределения потоков в операционной системе.Your application cannot control this because it depends on how the operating system schedules the threads.

В следующем примере поведение по умолчанию переопределяется путем включения оператора AsOrdered в исходную последовательность.The following example overrides the default behavior by using the AsOrdered operator on the source sequence. Это гарантирует, что метод Take всегда возвращает первые 1000 подходящих городов из исходной последовательности.This ensures that the Take method returns the first 1000 cities in the source sequence that meet the 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)

Однако этот запрос, скорее всего, выполняется медленнее неупорядоченной версии, так как он должен отслеживать исходный порядок во всех сегментах и контролировать его во время слияния.However, this query probably does not run as fast as the unordered version because it must keep track of the original ordering throughout the partitions and at merge time ensure that the ordering is consistent. Поэтому мы рекомендуем использовать AsOrdered только при реальной необходимости и только для тех частей запроса, в которых нужен строгий порядок.Therefore, we recommend that you use AsOrdered only when it is required, and only for those parts of the query that require it. Когда сохранение порядка больше не требуется, отключите его с помощью AsUnordered.When order preservation is no longer required, use AsUnordered to turn it off. В следующем примере мы создаем для этого два запроса.The following example achieves this by composing two queries.

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) { /*...*/ }
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

Обратите внимание, что в оставшейся части запроса PLINQ сохраняет порядок последовательности, созданный операторами с контролем порядка.Note that PLINQ preserves the ordering of a sequence produced by order-imposing operators for the rest of the query. Другими словами, операторы типа OrderBy и ThenBy обрабатываются так, как если бы после них стоял вызов AsOrdered.In other words, operators such as OrderBy and ThenBy are treated as if they were followed by a call to AsOrdered.

Операторы запросов и упорядочениеQuery Operators and Ordering

Следующие операторы запроса налагают сохранение порядка на все последующие операции запроса, пока не встретится вызов AsUnordered.The following query operators introduce order preservation into all subsequent operations in a query, or until AsUnordered is called:

Следующие операторы запроса PLINQ в некоторых случаях требуют упорядочения исходных последовательностей для получения правильных результатов.The following PLINQ query operators may in some cases require ordered source sequences to produce correct results:

Некоторые операторы запроса PLINQ ведут себя по-разному для упорядоченной и неупорядоченной исходной последовательности.Some PLINQ query operators behave differently, depending on whether their source sequence is ordered or unordered. Все они перечислены в следующей таблице.The following table lists these operators.

ОператорOperator Результат для упорядоченной исходной последовательностиResult when the source sequence is ordered Результат для неупорядоченной исходной последовательностиResult when the source sequence is unordered
Aggregate Недетерминированные выходные данные для неассоциативных или некоммутативных операцийNondeterministic output for nonassociative or noncommutative operations Недетерминированные выходные данные для неассоциативных или некоммутативных операцийNondeterministic output for nonassociative or noncommutative operations
All НеприменимоNot applicable НеприменимоNot applicable
Any НеприменимоNot applicable НеприменимоNot applicable
AsEnumerable НеприменимоNot applicable НеприменимоNot applicable
Average Недетерминированные выходные данные для неассоциативных или некоммутативных операцийNondeterministic output for nonassociative or noncommutative operations Недетерминированные выходные данные для неассоциативных или некоммутативных операцийNondeterministic output for nonassociative or noncommutative operations
Cast Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Concat Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Count НеприменимоNot applicable НеприменимоNot applicable
DefaultIfEmpty НеприменимоNot applicable НеприменимоNot applicable
Distinct Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
ElementAt Возвращает указанный элементReturn specified element Произвольный элементArbitrary element
ElementAtOrDefault Возвращает указанный элементReturn specified element Произвольный элементArbitrary element
Except Неупорядоченные результатыUnordered results Неупорядоченные результатыUnordered results
First Возвращает указанный элементReturn specified element Произвольный элементArbitrary element
FirstOrDefault Возвращает указанный элементReturn specified element Произвольный элементArbitrary element
ForAll Недетерминированные результаты параллельного выполненияExecutes nondeterministically in parallel Недетерминированные результаты параллельного выполненияExecutes nondeterministically in parallel
GroupBy Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
GroupJoin Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Intersect Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Join Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Last Возвращает указанный элементReturn specified element Произвольный элементArbitrary element
LastOrDefault Возвращает указанный элементReturn specified element Произвольный элементArbitrary element
LongCount НеприменимоNot applicable НеприменимоNot applicable
Min НеприменимоNot applicable НеприменимоNot applicable
OrderBy Переупорядочивает последовательностьReorders the sequence Начинает новый раздел с контролем порядкаStarts new ordered section
OrderByDescending Переупорядочивает последовательностьReorders the sequence Начинает новый раздел с контролем порядкаStarts new ordered section
Range Неприменимо (значение по умолчанию такое же, как для AsParallel)Not applicable (same default as AsParallel ) НеприменимоNot applicable
Repeat Неприменимо (значение по умолчанию такое же, как для AsParallel)Not applicable (same default as AsParallel) НеприменимоNot applicable
Reverse Изменяет порядок на обратныйReverses Ничего не делаетDoes nothing
Select Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Select (индексированный)Select (indexed) Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results.
SelectMany Упорядоченные результатыOrdered results. Неупорядоченные результатыUnordered results
SelectMany (индексированный)SelectMany (indexed) Упорядоченные результатыOrdered results. Неупорядоченные результатыUnordered results.
SequenceEqual Упорядоченное сравнениеOrdered comparison Неупорядоченное сравнениеUnordered comparison
Single НеприменимоNot applicable НеприменимоNot applicable
SingleOrDefault НеприменимоNot applicable НеприменимоNot applicable
Skip Пропускает первые n элементовSkips first n elements Пропускает любые n элементовSkips any n elements
SkipWhile Упорядоченные результатыOrdered results. Недетерминированный результат.Nondeterministic. Выполняет метод SkipWhile для текущего произвольного порядкаPerforms SkipWhile on the current arbitrary order
Sum Недетерминированные выходные данные для неассоциативных или некоммутативных операцийNondeterministic output for nonassociative or noncommutative operations Недетерминированные выходные данные для неассоциативных или некоммутативных операцийNondeterministic output for nonassociative or noncommutative operations
Take Отбирает первые n элементовTakes first n elements Отбирает любые n элементовTakes any n elements
TakeWhile Упорядоченные результатыOrdered results Недетерминированный результат.Nondeterministic. Выполняет метод TakeWhile для текущего произвольного порядкаPerforms TakeWhile on the current arbitrary order
ThenBy Дополняет OrderBySupplements OrderBy Дополняет OrderBySupplements OrderBy
ThenByDescending Дополняет OrderBySupplements OrderBy Дополняет OrderBySupplements OrderBy
ToArray Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
ToDictionary НеприменимоNot applicable НеприменимоNot applicable
ToList Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
ToLookup Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Union Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Where Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Where (индексированный)Where (indexed) Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results
Zip Упорядоченные результатыOrdered results Неупорядоченные результатыUnordered results

Неупорядоченные результаты не перемешиваются активно. Просто к ним не применяется логика для упорядочивания.Unordered results are not actively shuffled; they simply do not have any special ordering logic applied to them. В некоторых случаях неупорядоченный запрос может сохранять порядок исходной последовательности.In some cases, an unordered query may retain the ordering of the source sequence. Для запросов, использующих индексированный оператор Select, PLINQ гарантирует порядок возрастания индексов для выходных элементов, но не гарантирует конкретные индексы для этих элементов.For queries that use the indexed Select operator, PLINQ guarantees that the output elements will come out in the order of increasing indices, but makes no guarantees about which indices will be assigned to which elements.

См. такжеSee also