配列や文字列対する += 演算子
+=演算子
で 文字列の追加を行う場合に、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となる。