PowerShell スクリプトのパフォーマンスに関する考慮事項PowerShell scripting performance considerations

.NET を直接利用してパイプラインを回避する PowerShell スクリプトは、慣用的な PowerShell よりも高速になる傾向があります。PowerShell scripts that leverage .NET directly and avoid the pipeline tend to be faster than idiomatic PowerShell. 慣用的な PowerShell は、通常、コマンドレットと PowerShell 関数を頻繁に使用します。多くの場合、パイプラインを利用し、必要な場合にのみ .NET にドロップします。Idiomatic PowerShell typically uses cmdlets and PowerShell functions heavily, often leveraging the pipeline, and dropping down into .NET only when necessary.

注意

ここで説明する手法の多くは、PowerShell の慣用的なではないため、PowerShell スクリプトの読みやすさが低下する可能性があります。Many of the techniques described here are not idiomatic PowerShell and may reduce the readability of a PowerShell script. スクリプト作成者は、それ以外の場合は、慣用的な PowerShell を使用することをお勧めします。Script authors are advised to use idiomatic PowerShell unless performance dictates otherwise.

出力の抑制Suppressing Output

パイプラインへのオブジェクトの書き込みを回避するには、さまざまな方法があります。There are many ways to avoid writing objects to the pipeline:

$null = $arrayList.Add($item)
[void]$arrayList.Add($item)

への割り当て $null またはへのキャスト [void] はほぼ同じであり、通常はパフォーマンスが重要な場合に推奨されます。Assignment to $null or casting to [void] are roughly equivalent and should generally be preferred where performance matters.

$arrayList.Add($item) > $null

へのファイルのリダイレクト $null は、前の方法とほとんど同じですが、ほとんどのスクリプトでは違いがわかりません。File redirection to $null is nearly as good as the previous alternatives, most scripts would never notice the difference. このシナリオによっては、ファイルのリダイレクトによって若干のオーバーヘッドが生じます。Depending on the scenario, file redirection does introduce a little bit of overhead though.

$arrayList.Add($item) | Out-Null

パイプを別の方法と Out-Null 比較すると、大きなオーバーヘッドが発生します。Piping to Out-Null has significant overhead when compared to the alternatives. パフォーマンスに影響するコードでは、これを回避する必要があります。It should be avoiding in performance sensitive code.

$null = . {
    $arrayList.Add($item)
    $arrayList.Add(42)
}

スクリプトブロックを導入して (ドットソーシングまたはそれ以外の方法を使用して) 呼び出し、その結果をに割り当てること $null は、スクリプトの大きなブロックの出力を抑制するための便利な手法です。Introducing a script block and calling it (using dot sourcing or otherwise) then assigning the result to $null is a convenient technique for suppressing the output of a large block of script. この手法は、にパイプを使用して実行 Out-Null されるため、パフォーマンスに影響するスクリプトでは回避する必要があります。This technique performs roughly as well as piping to Out-Null and should be avoided in performance sensitive script. この例の追加のオーバーヘッドは、以前インラインスクリプトであったスクリプトブロックを作成して呼び出すことから取得されます。The extra overhead in this example comes from the creation of and invoking a script block that was previously inline script.

配列の加算Array Addition

多くの場合、項目のリストを生成するには、加算演算子を使用した配列を使用します。Generating a list of items is often done using an array with the addition operator:

$results = @()
$results += Do-Something
$results += Do-SomethingElse
$results

配列は不変であるため、これは非常に inefficent になる可能性があります。This can be very inefficent because arrays are immutable. 配列に追加するたびに、左側と右の両方のオペランドのすべての要素を保持するために十分な大きさの新しい配列が作成され、その後、両方のオペランドの要素が新しい配列にコピーされます。Each addition to the array actually creates a new array big enough to hold all elements of both the left and right operands, then copies the elements of both operands into the new array. 小さいコレクションの場合、このオーバーヘッドは問題にならない可能性があります。For small collections, this overhead may not matter. 大規模なコレクションの場合、これは明らかに問題になる可能性があります。For large collections, this can definitely be an issue.

代替手段がいくつかあります。There are a couple of alternatives. 実際に配列を必要としない場合は、代わりに ArrayList を使用することを検討してください。If you don't actually require an array, instead consider using an ArrayList:

$results = [System.Collections.ArrayList]::new()
$results.AddRange((Do-Something))
$results.AddRange((Do-SomethingElse))
$results

配列が必要な場合は、独自のを使用して、 ArrayList ArrayList.ToArray 配列が必要なときにを呼び出すだけで済みます。If you do require an array, you can use your own ArrayList and simply call ArrayList.ToArray when you want the array. または、PowerShell でとを作成することもでき ArrayList Array ます。Alternatively, you can let PowerShell create the ArrayList and Array for you:

$results = @(
    Do-Something
    Do-SomethingElse
)

この例では、PowerShell によって、 ArrayList 配列式内のパイプラインに書き込まれた結果を保持するが作成されます。In this example, PowerShell creates an ArrayList to hold the results written to the pipeline inside the array expression. をに割り当てる直前に、 $results PowerShell はをに変換し ArrayList object[] ます。Just before assigning to $results, PowerShell converts the ArrayList to an object[].

大きなファイルの処理Processing Large Files

PowerShell でファイルを処理する慣用的なの方法は、次のようになります。The idiomatic way to process a file in PowerShell might look something like:

Get-Content $path | Where-Object { $_.Length -gt 10 }

これは、.NET Api を直接使用するよりもはるかに低い順序で実行できます。This can be nearly an order of magnitude slower than using .NET APIs directly:

try
{
    $stream = [System.IO.StreamReader]::new($path)
    while ($line = $stream.ReadLine())
    {
        if ($line.Length -gt 10)
        {
            $line
        }
    }
}
finally
{
    $stream.Dispose()
}

Write-Host しないAvoid Write-Host

一般に、出力をコンソールに直接書き込むことは不適切であると考えられますが、意味がある場合は、多くのスクリプトでが使用さ Write-Host れます。It is generally considered poor practice to write output directly to the console, but when it makes sense, many scripts use Write-Host.

多くのメッセージをコンソールに書き込む必要がある場合は、 Write-Host よりもはるかに低い順序にすることができ [Console]::WriteLine() ます。If you must write many messages to the console, Write-Host can be an order of magnitude slower than [Console]::WriteLine(). ただし、 [Console]::WriteLine() は powershell.exe や powershell_ise.exe などの特定のホストに適した代替手段であることに注意してください。すべてのホストで動作する保証はありません。However, be aware that [Console]::WriteLine() is only a suitable alternative for specific hosts like powershell.exe or powershell_ise.exe - it's not guaranteed to work in all hosts.

を使用する代わり Write-Host に、 書き込み出力の使用を検討してください。Instead of using Write-Host, consider using Write-Output.