トップ回答者
AsSequentialメソッドの説明文の意味について

質問
-
こんにちは。AsSequentialメソッドについて質問です。
AsSequentialメソッドの説明文は次のようにあります。
ParallelQuery<TSource> を IEnumerable<T> に変換して、クエリの順次評価を強制的に実行します。
この部分の順次評価(sequential evaluation)という言葉の意味がよくわからなくて困っています。
以下のように、簡単な指定した範囲の数列を用意して、実行しましたが違いがよくわかりませんでした。
わかる人いましたら、教えてください。よろしくお願いします。
var data = Enumerable.Range(0, 40);
var result1 = numbers.AsParallel().Where(i => i % 2 == 0).AsSequential();
var result2 = numbers.AsParallel().Where(i => i % 2 == 0);
参考
https://msdn.microsoft.com/ja-jp/library/dd383949%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
回答
-
こんにちは。
以下のような感じでSelectでスリープさせてみたらわかりやすいのでは。
前者は順次実行されて、後者は並列実行されているようでした。
static void Main(string[] args) { var data = Enumerable.Range(0, 40); var result1 = data.AsParallel().Where(i => i % 2 == 0).AsSequential().Select((x) => { System.Threading.Thread.Sleep(100); return x; }).ToList(); var result2 = data.AsParallel().Where(i => i % 2 == 0).Select((x) => { System.Threading.Thread.Sleep(100); return x; }).ToList(); Console.WriteLine(string.Join(",", result1)); Console.WriteLine(string.Join(",", result2)); Console.Read(); }
- 回答としてマーク ichiethel 2016年1月27日 0:17
-
以下のようにすると、result1は並列に処理されて、result2は直列に(1つのスレッドで)処理されるということになるのを切り替える役割ということでしょうか。(AsSequentialが一番末尾にいるため)
LINQは実際に値を取り出す時に一つずつ動作しますが、それを1つのスレッドでやるか複数のスレッドでやるかの違いになります。その違いは、取り出す時の型によります。ちなみに、取り出すことは直列にしかできません。これは取り出す際に使用されるIEnumeratorが並列化に対応していないからです。よって正確には、取り出すための値を求める処理を1つのスレッドで行うか、複数のスレッドで行うかの違いになります。
result2はPallelQuery<int>型なので、Console.WriteLineで値を取り出す時に、その値を求める処理を複数スレッドで行います。つまり取り出す値は複数スレッドで計算されて取り出されます。しかし、先にも書いた通り、取り出す部分は1つのスレッドで順次処理されます。
result1はlEnumerable<int>型ですから、Console.WriteLineで値を取り出す時に、その値を求める処理を1つのスレッドで行います。
var result1 = numbers.AsParallel().Where(i => i % 2 == 0).AsSequential();
は、動作としては以下と同じになります。
var result1 = numbers.Where(i => i % 2 == 0);MSDNに書かれている通り、AsParallelはIEnumerableをParallelQueryに変換するものですし、AsSequentialはその逆になります。ポイントとしては、IEnumerableのところは直列処理しかできず、ParallelQueryのところは並列処理されるということです。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2016年1月26日 9:05 日本語修正
- 回答としてマーク ichiethel 2016年1月27日 0:17
-
もしかしてですが、 Where() の実体を誤解されていたりしないでしょうか?
シーケンシャルで動作するEnumerable.Where()の他にパラレルで動作するParallelEnumerable.Where()があります。通常のIEnumerable<T>では前者が使われます。AsParallel()でPrallelQuery<T>へ変換するとそれ以降は後者が使われるようになります。そしてAsSequential()がParallelQuery<T>からIEnumerable<T>へ変換する目的もそこにあります。
なお、ParallelQuery<T>はIEnumerable<T>を実装しているのでいつでもIEnumerable<T>へ変換することができます。
- 回答としてマーク ichiethel 2016年1月27日 0:17
-
ご回答頂いた内容を次のように咀嚼したつもりですが、今度こそあっているでしょうか?
基本的にあっていると思います。わかりやすく解説したページを見つけましたので、ご紹介しておきます。
このページにおいて、特に図におけるParallelEnumerable.Whereに注目して下さい。LINQは本当に強力だ (8) 何が並列化されるのか
http://www.kekyo.net/2012/12/02/254★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク ichiethel 2016年1月27日 5:02
すべての返信
-
こんにちは。
以下のような感じでSelectでスリープさせてみたらわかりやすいのでは。
前者は順次実行されて、後者は並列実行されているようでした。
static void Main(string[] args) { var data = Enumerable.Range(0, 40); var result1 = data.AsParallel().Where(i => i % 2 == 0).AsSequential().Select((x) => { System.Threading.Thread.Sleep(100); return x; }).ToList(); var result2 = data.AsParallel().Where(i => i % 2 == 0).Select((x) => { System.Threading.Thread.Sleep(100); return x; }).ToList(); Console.WriteLine(string.Join(",", result1)); Console.WriteLine(string.Join(",", result2)); Console.Read(); }
- 回答としてマーク ichiethel 2016年1月27日 0:17
-
佐祐理さん回答ありがとうございます。
クエリの順次評価とは、シーケンシャル(普通の1つのスレッドで処理するように明示できる?)という意味になるのでしょうか。
以下のようにすると、result1は並列に処理されて、result2は直列に(1つのスレッドで)処理されるということになるのを切り替える役割ということでしょうか。(AsSequentialが一番末尾にいるため)var result1 = numbers.AsParallel().Where(i => i % 2 == 0).AsSequential();
var result2 = numbers.AsParallel().Where(i => i % 2 == 0);foreach(int i in result1 or result2)
{
Console.WriteLine(i);
} -
以下のようにすると、result1は並列に処理されて、result2は直列に(1つのスレッドで)処理されるということになるのを切り替える役割ということでしょうか。(AsSequentialが一番末尾にいるため)
LINQは実際に値を取り出す時に一つずつ動作しますが、それを1つのスレッドでやるか複数のスレッドでやるかの違いになります。その違いは、取り出す時の型によります。ちなみに、取り出すことは直列にしかできません。これは取り出す際に使用されるIEnumeratorが並列化に対応していないからです。よって正確には、取り出すための値を求める処理を1つのスレッドで行うか、複数のスレッドで行うかの違いになります。
result2はPallelQuery<int>型なので、Console.WriteLineで値を取り出す時に、その値を求める処理を複数スレッドで行います。つまり取り出す値は複数スレッドで計算されて取り出されます。しかし、先にも書いた通り、取り出す部分は1つのスレッドで順次処理されます。
result1はlEnumerable<int>型ですから、Console.WriteLineで値を取り出す時に、その値を求める処理を1つのスレッドで行います。
var result1 = numbers.AsParallel().Where(i => i % 2 == 0).AsSequential();
は、動作としては以下と同じになります。
var result1 = numbers.Where(i => i % 2 == 0);MSDNに書かれている通り、AsParallelはIEnumerableをParallelQueryに変換するものですし、AsSequentialはその逆になります。ポイントとしては、IEnumerableのところは直列処理しかできず、ParallelQueryのところは並列処理されるということです。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2016年1月26日 9:05 日本語修正
- 回答としてマーク ichiethel 2016年1月27日 0:17
-
trapemiyaさん、佐祐理さん、Tak1waさん回答ありがとうございます。
ご回答頂いた内容を次のように咀嚼したつもりですが、今度こそあっているでしょうか?AsParallelはIEnumerableをParallelQueryに変換する。
AsSequentialはParallelQueryをIEnumerableに変換する。(IEnumerableは並列化しない)
取り出すための値を求める処理を1つのスレッドで行うか、複数のスレッドで行うかの違いの話。
メソッドの戻り値になる型を確認するとよかった。
次の1,2は同じ意味。
var result1 = numbers.AsParallel().Where(i => i % 2 == 0).AsSequential();
var result2 = numbers.Where(i => i % 2 == 0);
var result3 = numbers.AsParallel().Where(i => i % 2 == 0);
result1はIEnumerable<int>型 (AsSequentialで型が結果元に戻った)
result2はIEnumerable<int>型 (特になし)
result3はParallelQuery<int>型 (AsParallelで型を変換した)
ParallelQueryは、値を計算するところ(i => i%2 == 0のところ)が、複数スレッドで動いている。
わかりやすいのが次のTak1waさんのサンプル。
var result1 = data.AsParallel().Where(i => i % 2 == 0).AsSequential().Select((x) =>
{
System.Threading.Thread.Sleep(100);
return x;
}).ToList();
var result2 = data.AsParallel().Where(i => i % 2 == 0).Select((x) =>
{
System.Threading.Thread.Sleep(100);
return x;
}).ToList();
Console.WriteLine(string.Join(",", result1 or 2));
result1は1つのスレッドで処理するから、結果が出るのが遅かった。
result2は複数のスレッドで処理するから、結果が出るのが速かった。
ToListをしているから、すぐにコレクションを作って値を求める評価をしている。そこの値を求める処理をresult1 or 2のやり方でやっている差で速さに違いが出ている。 -
もしかしてですが、 Where() の実体を誤解されていたりしないでしょうか?
シーケンシャルで動作するEnumerable.Where()の他にパラレルで動作するParallelEnumerable.Where()があります。通常のIEnumerable<T>では前者が使われます。AsParallel()でPrallelQuery<T>へ変換するとそれ以降は後者が使われるようになります。そしてAsSequential()がParallelQuery<T>からIEnumerable<T>へ変換する目的もそこにあります。
なお、ParallelQuery<T>はIEnumerable<T>を実装しているのでいつでもIEnumerable<T>へ変換することができます。
- 回答としてマーク ichiethel 2016年1月27日 0:17
-
ご回答頂いた内容を次のように咀嚼したつもりですが、今度こそあっているでしょうか?
基本的にあっていると思います。わかりやすく解説したページを見つけましたので、ご紹介しておきます。
このページにおいて、特に図におけるParallelEnumerable.Whereに注目して下さい。LINQは本当に強力だ (8) 何が並列化されるのか
http://www.kekyo.net/2012/12/02/254★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク ichiethel 2016年1月27日 5:02