配列や文字列対する += 演算子
+=演算子で文字列の追加する場合、PowerShellの@()によるArrayは固定長なので配列をいちいち作り直します。
+= 演算子での操作であればArrayListやListでも変わりません。
これを回避するには、 +=演算子ではなくAddメソッドかStringBuilderを使う必要があります。
先に本件をまとめた記事がありますので、手元で確認しつつ見てみましょう。
PowerShell Performance: The += Operator (and When to Avoid It)
テスト概要
| 項目 | 内容 |
|---|---|
| 処理 | 1..任意の数分だけ、文字列を加える。任意の数は 100, 1000, 20000 |
| +=処理 | String, String[], ArrayList, List |
| Method処理 | ArrayList.Add(), List.Add(), StringBuilder.Append() |
※ [String[]]は[array]にするとArrayListやListと性能としては同一結果となる*1
ベンチメーク結果
+= 演算子
| += 演算子による処理 | 1..100結果 (ms) | 1..1000結果(ms) | 1..20000結果(ms) |
|---|---|---|---|
String |
1.334993 | 10.130783 | 3063.76350 |
String[] |
2.600920 | 180.654732 | 82795.10800 |
ArrayList |
1.141817 | 41.127059 | 17757.93110 |
List |
1.110024 | 42.076567 | 18247.20830 |
Method
| Merhod処理 | 1..100結果(ms) | 1..1000結果(ms) | 1..20000結果(ms) |
|---|---|---|---|
ArrayList.Add() |
0.520307 | 4.739987 | 109.12396 |
List.Add() |
0.485547 | 4.400812 | 91.80697 |
StringBuilder.Append() |
0.528979 | 4.703192 | 96.20759 |
テストコード
GitHubにも置いてあります。
Benchmark_SOperatorAndMethodToString
+= 演算子
function Get-StringForeachTest{ param( $int ) [string]$string = "" measure-Command{ foreach ($i in $int) { $addString = "hello $i" $string += $addString } $string } # $string.GetType().FullName } function Get-ArrayForeachTest{ param( $int ) [string[]]$array = @() measure-Command{ foreach ($i in $int) { $addString = "hello $i" $array += $addString } $array } # $array.GetType().FullName } function Get-ArrayListForeachTest{ param( $int ) $arraylist = New-Object System.Collections.ArrayList measure-Command{ foreach ($i in $int) { $addString = "hello $i" $arraylist += $addString } $arraylist } # $arraylist.GetType().FullName } function Get-ListForeachTest{ param( $int ) $list = New-Object 'System.Collections.Generic.List[System.String]' measure-Command{ foreach ($i in $int) { $addString = "hello $i" $list += $addString } $list } # $list.GetType().FullName }
Method
function Get-ArrayListAddForeachTest{ param( $int ) $arraylist = New-Object System.Collections.ArrayList Measure-Command{ foreach ($i in $int) { $addString = "hello $i" $arraylist.Add($addString) > $null } $arraylist.ToArray() } # $arraylist.GetType().FullName } function Get-ListAddForeachTest{ param( $int ) $list = New-Object 'System.Collections.Generic.List[System.String]' measure-Command{ foreach ($i in $int) { $addString = "hello $i" $list.Add($addString) } $list.ToArray() } # $list.GetType().FullName } function Get-StringBuilderForeachTest{ param( $int ) $stringBuilder = New-Object System.Text.StringBuilder Measure-Command{ foreach ($i in $int) { $addString = "hello $i" $stringBuilder.Append($addString) > $null } $stringBuilder.ToString() } # $stringBuilder.GetType().FullName }
テスト実行コード
20000回のテストに利用したコードです。
+= 演算子
$max = 20000 # += Operator Write-Host "String += Operator 1..$max test" -ForegroundColor Cyan Get-StringForeachTest -int (1..$max) | Measure-Object -Property TotalMilliseconds -Average | select -ExpandProperty Average Write-Host "Array += Operator 1..$max test" -ForegroundColor Cyan Get-ArrayForeachTest -int (1..$max) | Measure-Object -Property TotalMilliseconds -Average | select -ExpandProperty Average Write-Host "ArrayList += Operator 1..$max test" -ForegroundColor Cyan Get-ArrayListForeachTest -int (1..$max) | Measure-Object -Property TotalMilliseconds -Average | select -ExpandProperty Average Write-Host "List += Operator 1..$max test" -ForegroundColor Cyan Get-ListForeachTest -int (1..$max) | Measure-Object -Property TotalMilliseconds -Average | select -ExpandProperty Average
Method
# Method Write-Host "ArrayList Add Method 1..$max test" -ForegroundColor Green 1..10 | %{Get-ArrayListAddForeachTest -int (1..$max)} | Measure-Object -Property TotalMilliseconds -Average | select -ExpandProperty Average Write-Host "List Add Method 1..$max test" -ForegroundColor Green 1..10 | %{Get-ListAddForeachTest -int (1..$max)} | Measure-Object -Property TotalMilliseconds -Average | select -ExpandProperty Average Write-Host "StringBuilder Append Method 1..$max test" -ForegroundColor Green 1..10 | %{Get-StringBuilderForeachTest -int (1..$max)} | Measure-Object -Property TotalMilliseconds -Average | select -ExpandProperty Average
まとめ
当然といえば当然の結果ですが、ベンチマークの結果は一目瞭然です。
PowerShellでは、サクサク書けるようなゆるふわ感がありますが、意識しないといけないところもあるので気を付けてください。
特に+=は危なく、驚くほど処理にコストがかかります。
私としては、ListかStringBuilderを使うことをおすすめします。
*1:[String]は、[Array]とした場合、ArrayListやListへの += と同様にObjectとなり性能も同一となります。つまり[Array]としたほうが[String]よりも高速ですが、型がObjectとなる。