PowerShellのパイプラインは手元で使っている分には凄い便利ですが、大量のデータを扱うとしばしばパフォーマンスがとても遅くなる場合があります。
PowerShellのWhere-Objectは便利なので使うことが多いと思います。しかし大きな処理をすると問題になることが多い部分です。例えば手元にあった以下のようなスクリプトの場合はWhere-Objectをforeachで書き換えただけで5分の1程度に処理時間が短縮できました。
Whereパターン
$filteredEntries = $10000Entries | Where{($_.Key -eq $Key) -and ($_.Id -eq $id) -and ($_.Value)}
書き直しパターン
foreach($e in $10000Entries) { if(($e.Key -eq $Key) -and ($e.Id -eq $id) -and ($e.Value)) { $filteredEntries += $e } }
計測してみた
しっかり計測してみました。
$loop = 10000 $randomNums = @() for ($i = 1; $i -lt $loop; $i++) { $randomNums += Get-Random -Maximum 99999 -Minimum 10000 } $nums1 = @() $nums2 = @() $nums3 = @() $result1 = (Measure-Command{ $nums1 = $randomNums | Where-Object{$_ -match "23"} }) $result2 = (Measure-Command{ #この方法だけSystem.Collections.ObjectModel.Collection`の型になります $nums2 = $randomNums.Where({$_ -match "23"}) }) $result3 = (Measure-Command{ foreach ($num in $randomNums) { if($num -match "23") { $nums3 += $num } } }) "1. Where-Object :{0}" -f $result1.TotalMilliseconds "2. where Method :{0}" -f $result2.TotalMilliseconds "3. foreach句 :{0}" -f $result3.TotalMilliseconds
結果は下の表の通り明らかにforeachステートメントが一番早いですね。ISEにコピペで検証できるのでやってみてください。
No | 方法 | 1回目 | 2回目 | 3回目 |
---|---|---|---|---|
1 | Where-Object | 315.2737 | 407.667 | 324.9565 |
2 | where Method | 103.7537 | 136.2756 | 106.9086 |
3 | foreach句 | 54.4612 | 61.2299 | 46.6986 |
まとめ
コンソールからダイアログで作業する分にはとても便利なパイプラインですが、大きな処理の場合は無視できない遅延を招く可能性があります。その場合は、foreach句の利用も検討した方が良いかもしれません。