none
AsSequentialメソッドの説明文の意味について RRS feed

  • 質問

  • こんにちは。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

    2016年1月26日 7:39

回答

  • こんにちは。

    以下のような感じで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
    2016年1月26日 8:00
    モデレータ
  • 以下のようにすると、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/


    2016年1月26日 8:57
    モデレータ
  • もしかしてですが、 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
    2016年1月26日 13:22
  • ご回答頂いた内容を次のように咀嚼したつもりですが、今度こそあっているでしょうか?

    基本的にあっていると思います。わかりやすく解説したページを見つけましたので、ご紹介しておきます。
    このページにおいて、特に図における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
    2016年1月27日 0:34
    モデレータ
  • いえ違います。先に書いた通り .Where() が並列に実行されるのであって、result1やresult2は直列に実行されます。

    この違いはTak1waさんのサンプルがわかりやすいと思います。

    • 回答としてマーク ichiethel 2016年1月27日 0:17
    2016年1月26日 8:15

すべての返信

  • numbers    // シーケンシャルに実行する
        .AsParallel()  // 以降をパラレルに実行する
        .Where(i => i % 2 == 0)    // パラレルに実行する
        .AsSequential();    // 以降をシーケンシャルに実行する

    という切り替えなのですが、どの辺りがわからなかったでしょうか?

    速度差という点では、パラレルに実行すれば必ず高速化されるものではなく、スレッド管理などのオーバーヘッドで逆に遅くなることもあります。

    2016年1月26日 7:54
  • こんにちは。

    以下のような感じで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
    2016年1月26日 8:00
    モデレータ
  • 佐祐理さん回答ありがとうございます。
    クエリの順次評価とは、シーケンシャル(普通の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);
    }

    2016年1月26日 8:09
  • いえ違います。先に書いた通り .Where() が並列に実行されるのであって、result1やresult2は直列に実行されます。

    この違いはTak1waさんのサンプルがわかりやすいと思います。

    • 回答としてマーク ichiethel 2016年1月27日 0:17
    2016年1月26日 8:15
  • 以下のようにすると、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/


    2016年1月26日 8:57
    モデレータ
  • 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のやり方でやっている差で速さに違いが出ている。

    2016年1月26日 9:39
  • もしかしてですが、 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
    2016年1月26日 13:22
  • 佐祐理さん回答ありがとうございます。
    実体の誤解というのか、こうした実装の違いがあることを知りませんでした。

    Where(IEnumerable)
    述語に基づいて値のシーケンスをフィルター処理します。
    Where(ParallelEnumerable)
    述語に基づいて値のシーケンスを並列でフィルター処理します。

    この部分ではっきりフィルターと、並列でのフィルターという違いがあったのですね。
    とても理解する助けになりました。ありがとうございました。

    2016年1月27日 0:23
  • ご回答頂いた内容を次のように咀嚼したつもりですが、今度こそあっているでしょうか?

    基本的にあっていると思います。わかりやすく解説したページを見つけましたので、ご紹介しておきます。
    このページにおいて、特に図における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
    2016年1月27日 0:34
    モデレータ