以下の内容はhttps://tech.guitarrapc.com/entry/2013/09/22/100203より取得しました。


PowerShell の +=演算子 パフォーマンス と その回避方法

配列や文字列対する += 演算子

+=演算子で文字列の追加する場合、PowerShellの@()によるArrayは固定長なので配列をいちいち作り直します。

+= 演算子での操作であればArrayListListでも変わりません。

これを回避するには、 +=演算子ではなく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では、サクサク書けるようなゆるふわ感がありますが、意識しないといけないところもあるので気を付けてください。

特に+=は危なく、驚くほど処理にコストがかかります。

私としては、ListStringBuilderを使うことをおすすめします。

*1:[String]は、[Array]とした場合、ArrayListやListへの += と同様にObjectとなり性能も同一となります。つまり[Array]としたほうが[String]よりも高速ですが、型がObjectとなる。




以上の内容はhttps://tech.guitarrapc.com/entry/2013/09/22/100203より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14