2012年6月5日火曜日

PowerShellのパイプライン処理の並列性

PowerShell覚え書き

背景

ここ数週間PowerShellを少し触ってみて、動作中になにか違和感を覚えた瞬間が何度かありました。どうもパイプラインとプロセス/スレッドの対応関係が関連していそうなのでミニ検証をしてみます。PowerShell3にはワークフローや並列処理の記述が増えるということなので、まだシンプルなうちに根本的なところで理解を深めておきたいと思います。おそらく同じような検証をやっている人はたくさんいるでしょうけど、ネット上で検索してもうまく見つからなかったので。

パイプライン処理の並列性

UNIXのシェルスクリプトの場合には複数のコマンドをパイプラインでつなぐと、各コマンドのプロセスが起動され複数のデータを並列的に処理しますが、PowerShellではどうでしょうか。例えば、以下の処理時間は何秒でしょうか。
1..3|%{sleep 1;$_ }|%{sleep 5;$_}
これはUNIXの場合には約16秒(=1+5*3)なのですが、PowerShellの場合にはMeasure-Commandで実測すると約18秒になりました。
えっ、嘘でしょうと私も疑ったのですが、結果から見る限りそれが正しいようです。つまりPowerShellの場合にはパイプライン部分が並列的に処理はなされていないということになってしまいます。
例えば、データA、B、Cに対して処理ステップ1、2、3を実行するとした場合(各ステップの処理時間が同じ長さで、集約処理はしないと仮定)を図で表すとこういうことです。

想定:

=====================>時間
A1 → A2 → A3
_____B1 → B2 → B3
__________C1 → C2 → C3

実際:

====================================>時間
A1 → A2 → A3
______________B1 → B2 → B3
____________________________C1 → C2 → C3


追加検証

それでも信じられないので以下も試してみましたが、確かに上記の通りでした。また、さらにbegin,endの実行タイミングも想定と異なりました。
function step([int]$num,[int]$interval)
{
    begin{
        write-host((&"get-date").tostring() +" start(" + $num.tostring() +")")
    }
    process{ 
        write-host ((&"get-date").tostring() +" step(" + $num.ToString() +") " +$_ + " |")
        sleep $interval
        write-host ((&"get-date").tostring() +" step(" + $num.ToString() +") " +$_ + " V")
        "[" + $_ + "]"
    }
    end {
        write-host((&"get-date").tostring() +" finished(" + $num.tostring() +")")
    }
}

("A","B","C")|step 1 3| step 2 2 | step 3 1 
実行結果
2012/06/05 21:26:04 start(1) #=>beginは最初にまとめて実行される
2012/06/05 21:26:04 start(2) #=>beginは最初にまとめて実行される
2012/06/05 21:26:04 start(3) #=>beginは最初にまとめて実行される
2012/06/05 21:26:04 step(1) A |
2012/06/05 21:26:08 step(1) A V
2012/06/05 21:26:08 step(2) [A] |
2012/06/05 21:26:10 step(2) [A] V
2012/06/05 21:26:10 step(3) [[A]] |
2012/06/05 21:26:11 step(3) [[A]] V
[[[A]]]
2012/06/05 21:26:11 step(1) B |
2012/06/05 21:26:14 step(1) B V
2012/06/05 21:26:14 step(2) [B] |
2012/06/05 21:26:16 step(2) [B] V
2012/06/05 21:26:16 step(3) [[B]] |
2012/06/05 21:26:17 step(3) [[B]] V
[[[B]]]
2012/06/05 21:26:17 step(1) C |
2012/06/05 21:26:20 step(1) C V
2012/06/05 21:26:20 step(2) [C] |
2012/06/05 21:26:22 step(2) [C] V
2012/06/05 21:26:22 step(3) [[C]] |
2012/06/05 21:26:23 step(3) [[C]] V
[[[C]]]
2012/06/05 21:26:23 finished(1) #=>endは最後にまとめて実行される
2012/06/05 21:26:23 finished(2) #=>endは最後にまとめて実行される
2012/06/05 21:26:23 finished(3) #=>endは最後にまとめて実行される

結論

PowerShellのパイプライン記号の処理についてはいわゆる複数プロセスや複数スレッドでの並列処理ではなく、単一スレッドによる逐次処理である(少なくともそうなる場合がある)としか考えられない。これは私としてはショックです。オーマイガー!アイヤー吃驚了!
当初はモナドシェルと呼ばれていたPowerShellのことですから、関数型言語固有の理由があるのかもしれません。どなたか誤りがあればぜひご指摘くださいませ。

0 件のコメント:

コメントを投稿