为什么powershell如此缓慢地验证巨大的byte []数组?

时间:2018-02-21 00:59:36

标签: performance powershell parameter-passing

我花了一些时间试图在PowerShell应用程序中找到瓶颈,而没有怀疑它只是一个缓慢的参数验证。示例代码说明了问题:

function Test-ValidatePerformance
{
    param(
        [ValidateNotNullOrEmpty()]
        [Byte[]]
        $Data
    )

    $sw.Stop()

    Write-Host "Executing after $([Math]::Round($sw.Elapsed.TotalMilliSeconds))ms"
}

function Test-NoValidatePerformance
{
    param(
        [Byte[]]
        $Data
    )

    $sw.Stop()

    Write-Host "Executing after $([Math]::Round($sw.Elapsed.TotalMilliSeconds))ms"
}

$buf = [IO.File]::ReadAllBytes('C:\17MB_FILE.bin')

Write-Host "Calling with validation..."
$sw = [Diagnostics.Stopwatch]::StartNew()
Test-ValidatePerformance $buf

Write-Host "`nCalling without validation..."
$sw = [Diagnostics.Stopwatch]::StartNew()
Test-NoValidatePerformance $buf

输出:

Calling with validation...
Executing after 1981ms

Calling without validation...
Executing after 3ms

我的问题是:为什么[ValidateNotNullOrEmpty()]如此缓慢(考虑到它的名称状态)它只检查null或空参数?

1 个答案:

答案 0 :(得分:4)

当您将(大多数)验证属性添加到集合时,它将应用于集合中的每个项目;而不是整个集合,因此将针对每个字节运行验证。

mklement0提出an open issue on GitHub about this very thing

测试它不是空的最简单方法就是使参数成为必需参数;那时将不接受空数组:

function Test-ValidatePerformance
{
    param(
        [Parameter(Mandatory)]
        [Byte[]]
        $Data
    )

    $sw.Stop()

    Write-Host "Executing after $([Math]::Round($sw.Elapsed.TotalMilliSeconds))ms"
}

注意:正如原始海报所指出的那样,Patrick Meinecke已确认in this GitHub issue, there is a bug in Windows PowerShell (fixed in Core), regarding the performance issue with Mandatory parameters

如果您希望参数是可选的,但如果提供的参数不能为空,则可以改为使用[ValidateCount()],这应该很快:

function Test-ValidatePerformance
{
    param(
        [ValidateCount(1,[int]::MaxValue)]
        [Byte[]]
        $Data
    )

    $sw.Stop()

    Write-Host "Executing after $([Math]::Round($sw.Elapsed.TotalMilliSeconds))ms"
}

或者您可以执行检入代码而不是使用验证属性。

function Test-ValidatePerformance
{
    param(
        [Byte[]]
        $Data
    )

    if (-not $Data -and $PSBoundParameters.ContainsKey('Data')) {
        throw [System.ArgumentException]'An empty array is not allowed'
    }

    $sw.Stop()

    Write-Host "Executing after $([Math]::Round($sw.Elapsed.TotalMilliSeconds))ms"
}