PLINQ における順序維持Order 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. 詳細については、「Understanding Speedup in PLINQ (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. 2 つのクエリを組み合わせてこの処理を行う例を次に示します。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. つまり、OrderByThenBy の演算子に続いて 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 OrderBy を補足Supplements OrderBy OrderBy を補足Supplements OrderBy
ThenByDescending OrderBy を補足Supplements OrderBy OrderBy を補足Supplements 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