ForEach-Objectの並列処理機能

MicrosoftはPowerShell 7.0.0 Preview3の段階でForEach-Objectコマンドレットに並列処理機能を追加した。この機能を利用するとブロック単位で処理を並列化することができる。並列数は制御が可能で、使い方によっては大幅な高速化が可能になることを以前取り上げた。

こうした並列処理の活用はUNIX系のシェルでは気にする必要がない。UNIX系のシェルではパイプで接続したプロセスはそれぞれが個別に同時に処理される。その性能はカーネルのスケーラビリティの高さに依存することになるが、最近のカーネルはこの辺りのチューニングがよく行われており、高いスケーラビリティを持っている。パイプで接続した分だけ、並列処理が進むと考えておいてそれほど間違ってはいない状況にある(もちろんパイプを流れるデータの密度と言うか、「どの程度スムーズにデータが流れるか」に依存することにはなる)。

これがPowerShell Coreの場合にはそうは行かない。PowerShell Coreは見た目はUNIXのシェルに似ているが、実際にはオブジェクト指向のプログラミング言語としての特性が強く、パイプを流れるデータはすべての処理が終わってからオブジェクトが渡される。UNIXのシェルのように、「パイプで接続したら処理が並列化される」というわけではないのである。

この点、ForEach-Objectコマンドレットに導入された並列処理機能は興味深い。生成にオーバーヘッドが存在するため、処理時間が短いものに適用すると逆効果になるが、長い処理や待ちが発生するタイプの処理は、ForEach-Objectコマンドレットの並列処理機能を使うことで大幅な高速化が可能になる。この機能はPowerShell 7における大きな特徴になると見られる。

PowerShell 7.0.0 Preview4でもちょっぴり改善

ForEach-Objectコマンドレットの並列処理性能は、PowerShell 7.0.0 Preview4でわずかに改善した。PowerShell 7.0.0 Preview3の段階のForEach-Objectコマンドレットは、同時に実行される実行スペース(runspace)の数が多くなるとパフォーマンスが悪化するという問題があった。問題の詳細については以下のページに情報がまとまっている。


PowerShell 7.0.0 Preview4にはこの修正が取り込まれており、同時に実行される実行スペースの数が多くなった場合でもパフォーマンスが低下しないようになっている。例えば、次の実行結果はPowerShell 7.0.0 Preview3とPowerShell 7.0.0 Preview4で同じ処理を行ったものだ。

PowerShell 7.0.0 Preview3における実行例

PS C:\Users\daich> Measure-Command { 1..254 | foreach -Parallel {ping "192.168.0.$_" -n 1 | where {$_ -match "ttl="}} -ThrottleLimit 300 }
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 56
Milliseconds      : 994
Ticks             : 569945783
TotalDays         : 0.000659659471064815
TotalHours        : 0.0158318273055556
TotalMinutes      : 0.949909638333333
TotalSeconds      : 56.9945783
TotalMilliseconds : 56994.5783


PS C:\Users\daich>

PowerShell 7.0.0 Preview4における実行例

PS C:\Users\daich> Measure-Command { 1..254 | foreach -Parallel {ping "192.168.0.$_" -n 1 | where {$_ -match "ttl="}} -ThrottleLimit 300 }
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 13
Milliseconds      : 923
Ticks             : 139235134
TotalDays         : 0.000161151775462963
TotalHours        : 0.00386764261111111
TotalMinutes      : 0.232058556666667
TotalSeconds      : 13.9235134
TotalMilliseconds : 13923.5134


PS C:\Users\daich>

結果を表とグラフにまとめると次のようになる。

バージョン 7.0.0-Preview3 7.0.0-Preview4
処理時間[秒] 56.99 13.92

PowerShell 7.0.0 Preview3 / Preview4における処理時間比較グラフ

簡単な比較だが、それでも4倍ほど処理時間が高速になっていることが確認できる。どこまで高速化されるかは実行する環境によって左右されるので、場合によってはもっと大きな高速化が確認できるものもあるだろう。

ForEach-ObjectコマンドレットについてはPowerShell 7.0.0 Preview4でほかの処理に関してもパフォーマンスの改善が実施されている。重要度の高いコマンドレットであるだけに嬉しい改善だ。

高速化が進めば採用のシーンも広がる

Windows PowerShellはPowerShell 7に置き換わる予定だ。Windows Terminalも同時期に最初のバージョンがリリースされることになる。2020年5月または6月に実施されると見られるWindows 10フィーチャーアップデート以降、Windows 10におけるPowerShellの重要度は現在よりも高くなるものと見られる。

PowerShell 7はmacOSでもLinuxでも利用できる。現在のところパフォーマンスの改善についてはmacOSやLinuxではそれほど恩恵が得られない状況のようだが、この点もいずれ改善が進むだろう。PowerShellは強力なシェルである反面、処理速度が遅いという課題も抱えている。PowerShell 7からはその問題も多少だが軽減されることになる。将来的にPowerShellがどのような分野で利用されていくようになるのか、今後の展開が楽しみだ。

参考資料