【问题标题】:PowerShell - Binary Files Slow PerformancePowerShell - 二进制文件性能缓慢
【发布时间】:2014-06-04 20:40:15
【问题描述】:

我已经编写了以下解析二进制文件的代码

Param(
    [Parameter(Mandatory=$True)]
    [string]$inputFilePath
)

function GetLengthFrom2Byte
{
    Param(
        [Parameter(Mandatory=$True)]
        [byte[]]$bytes
    )

    if ($bytes.Length -ne 2)
    {
        echo "Parametro di Input non valido"    
    }
    else
    {
        $lenByte = New-Object Byte[](2)

        $lenByte[0] = $bytes[1]
        $lenByte[1] = $bytes[0]

        return [BitConverter]::ToUInt16($lenByte, 0)
    }
}

try
{
    if (!(Test-Path($inputFilePath)))
    {
        Throw "File di Input non valido: <{0}>" -f $inputFilePath    
    }

    $inputStream = New-Object IO.FileStream($inputFilePath, [IO.FileMode]::Open,  [IO.FileAccess]::Read, [IO.FileShare]::Read)
    $inputBinaryReader = New-Object IO.BinaryReader($inputStream)

    while ($inputBinaryReader.PeekChar() -ne -1)
    {
        $AfpHeader = $inputBinaryReader.ReadByte()

        if ($AfpHeader -ne 0x5A)
        {
            Throw "Errore nella struttura AFP. Byte 0x5A non trovato all' offset: <{0}>" -f $inputBinaryReader.BaseStream.Position
            exit 8
        }

        $AfpLength = $inputBinaryReader.ReadBytes(2)

        $recordLength = GetLengthFrom2Byte($AfpLength)

        $inputBinaryReader.ReadBytes($recordLength - 2) > $null
    }

    echo "File AFP Validato"
}
catch [Exception]
{
    echo "Errore: {0}" -f $error[0]
    exit 8
}
finally
{
    $inputBinaryReader.Dispose()
    $inputStream.Dispose()
}

exit 0

我不想详细介绍二进制解析。 问题是 C# 中的相同函数需要大约 50 秒,而在 Powershell 中需要 11 分钟。

由于我使用的是相同的课程,我不知道为什么差距如此之大。 有什么方法可以提高 powershell 性能?

【问题讨论】:

  • 到底是什么问题?
  • 您明白吗,C# 是编译的,而 PowerShell 是解释的?
  • 我们说的是每一个字节循环的 12 倍……对我来说似乎很合理。
  • 如果问题是如何提高性能。使用 Add-Type 命令创建的内联 C#。
  • Add-Type @'&lt;define a static class here in C#&gt;'@。然后从您的脚本中调用此类的静态方法。见get-help Add-Type,那里有一个例子。

标签: c# .net powershell scripting binaryfiles


【解决方案1】:

如果将此行移至脚本范围,您将获得最大的性能提升:

    $lenByte = New-Object Byte[](2)

所以它只发生一次。然后你应该将引用更改为 '$script:lenByte'。

如果您跳过创建本地函数 GetLengthFrom2Byte 而只是内联该位脚本,您将获得另一个性能提升。

在这 2 处更改之后,我认为性能应该更符合 C#。

从 PowerShell V3 开始,脚本和循环在执行 16 次后确实会编译为本机代码,因此可以实现接近 C# 的性能,但您必须避免某些 PowerShell 功能,这在许多情况下是不可能做到的。

调用 PowerShell cmdlet 就像 New-Object 相当昂贵,因为 PowerShell 每次执行时都会执行以下操作:

  • 每次执行命令时都会搜索该命令。对于 New-Object,搜索会在多个范围内查找多种不同的命令类型,然后再找到 cmdlet。
  • 绑定参数(使用反射)
  • 将字符串参数转换为 System.Type 实例
  • 确定要调用的构造函数
  • 通过反射调用构造函数

请注意,上述所有操作都可以显着加快,只是还没有发生。

PowerShell 必须为您的本地函数 GetLengthFrom2Byte 执行类似的操作,但它稍微好一点,因为该命令需要搜索的范围更少。参数还是需要转换的,不过转换2个元素的数组可能比字符串到类型的转换要快很多。

请注意,即使在使用纯 .Net 样式代码(内部循环中没有使用 PowerShell 函数)之后,由于 PowerShell 的动态特性,性能仍然会比 C# 差。例如,每次访问属性或调用 .Net 方法时,PowerShell 都必须检查目标对象的类型和方法参数的类型。在 V3 中,这些操作比 V2 快得多,但仍然必须进行动态检查以确保 PowerShell 语言语义。

【讨论】:

    猜你喜欢
    • 2013-11-28
    • 2011-03-31
    • 2017-03-28
    • 2013-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 2010-10-14
    相关资源
    最近更新 更多