有没有一种方法可以加速在ps-objects中进行动态成员查找?

3
在以下代码中,大多数时间都花在$v = $proc.$columnName这行上,我想知道是否有一种方法可以加速查找对象成员的值。
在下面的代码中,我选择$objs作为get-process的结果,但是在我的情况下,$objs可以是任何类型的对象数组,因此需要动态查找对象的成员。
$objs= get-process

$columnNames = @()
foreach ($member in ($objs | get-member -memberType property, noteproperty)) {
      [string]$name = $member.name
      $columnNames += $name
}

[Int64 ] $sum = 0
[string] $columnName = ''
foreach ($obj in $objs) {

    foreach ($columnName in $columnNames) {

        $v = $obj.$columnName
#       $v = $obj.psObject.members.Item($columnName).value

        if ($v -eq $null) {
        }
        elseif ($v -is [System.IntPtr]) {
            $sum = $sum + ($v -as [int64] )
        }
        elseif ($v -is [System.Int64] -or $v -is [System.Int32]) {
            $sum = $sum + $v
        }
    }
}

"sum = $sum"

@Theo,是的,现在已经修复了。 - René Nyffenegger
不是一个通用解决方案,但在这种情况下,您可以通过事先按类型过滤要包含的成员来加快速度。 - marsze
我认为这行代码 $v = $obj.$columnName 是不必要的,你可以直接在你的代码中使用 obj.$columnName。 - Farbkreis
2个回答

1
这是一个名为.PSObject的PSObjects隐藏属性,包括我在内的太多人都不知道,尽管我花费了数千小时来使用PowerShell。从那里,你可以使用.where显著提高过滤性能。
New-Variable -Force -Name:'Processes' -value:(Get-Process)
New-Variable -Force -Name:'Sum' -Value:([Int64]$Null)
ForEach ($Process in $Processes) {
    $Process.PSObject.Properties.Where({
        ($_.MemberType -in @('Property','NoteProperty')) -and 
        ($_.TypeNameOfValue -in @('System.IntPtr','System.Int64','System.Int32')) -and
        (-Not [String]::IsNullOrWhiteSpace($_.value))
    }) |ForEach-Object {
        $Sum = $Sum + ($_.value -as [Int64])
    }
}
"Sum = $Sum"

比较结果

Name                           Value                                                                                                                                                                                                                                           
----                           -----                                                                                                                                                                                                                                           
BlockBTime                     9.18                                                                                                                                                                                                                                            
SameResult                     True                                                                                                                                                                                                                                            
BlockATime                     1.52                                                                                                                                                                                                                                            
BlockASum                      1037197387512388                                                                                                                                                                                                                                
Difference                     Block A (New Code) is ~7.66s faster than Block B (Old Code)                                                                                                                                                                                     
BlockBSum                      1037197387512388

验证与比较

#Stopwatch
New-Variable -Force -Name:'StopWatch' -Value:(New-Object -TypeName 'System.Diagnostics.Stopwatch')
New-Variable -Force -Name:'Result' -Value:@{
    BlockATime = [Int]0
    BlockASum = [Int64]0

    BlockBTime = [Int]0
    BlockBSum = [Int64]0

    Difference = $Null
    SameResult = $Null
}

New-Variable -Force -Name:'Processes' -value:(Get-Process)

$StopWatch.Restart()
New-Variable -Force -Name:'Sum' -Value:([Int64]$Null)
ForEach ($Process in $Processes) {
    $Process.PSObject.Properties.Where({
        ($_.MemberType -in @('Property','NoteProperty')) -and 
        ($_.TypeNameOfValue -in @('System.IntPtr','System.Int64','System.Int32')) -and
        (-Not [String]::IsNullOrWhiteSpace($_.value))
    }) |ForEach-Object {
        $Sum = $Sum + ($_.value -as [Int64])
    }
}

$Result.BlockATime = [math]::Round($StopWatch.Elapsed.TotalSeconds,2)
$Result.BlockASum = $Sum




$objs= $Processes
$StopWatch.Restart()
$columnNames = @()
foreach ($member in ($objs | get-member -memberType property, noteproperty)) {
      [string]$name = $member.name
      $columnNames += $name
}

[Int64 ] $sum = 0
[string] $columnName = ''
foreach ($obj in $objs) {

    foreach ($columnName in $columnNames) {

        $v = $obj.$columnName
#       $v = $obj.psObject.members.Item($columnName).value

        if ($v -eq $null) {
        }
        elseif ($v -is [System.IntPtr]) {
            $sum = $sum + ($v -as [int64] )
        }
        elseif ($v -is [System.Int64] -or $v -is [System.Int32]) {
            $sum = $sum + $v
        }
    }
}
$Result.BlockbTime = [math]::Round($StopWatch.Elapsed.TotalSeconds,2)
$Result.BlockbSum = $Sum

#Which block is faster?
If ($Result.BlockATime -lt $Result.BlockBTime) {
    $Result.Difference = "Block A (New Code) is ~$(($Result.BlockBTime - $Result.BlockATime))s faster than Block B (Old Code)"
} Else {
    $Result.Difference = "Block A (New Code) is ~$(($Result.BlockBTime - $Result.BlockATime))s Slower than Block B (Old Code)"
}

#Are the results the same?
If ($Result.BlockASum -eq $Result.BlockBSum) {
    $Result.SameResult = $True
}

1
也许有更多的方法来加快这个过程,但是下面我已经去掉了不必要的部分:
$objs= Get-Process

$columnNames = ($objs | Get-Member -MemberType property, noteproperty).Name

[Int64 ] $sum = 0
foreach ($obj in $objs) {
    foreach ($v in $columnNames) {
        if ($obj.$v -as [int64]) { $sum += [int64]$obj.$v }
    }
}

"sum = $sum"

我非常确定$obj.$v会使循环变慢,因此仅仅去掉不必要的部分只能稍微提高性能。在我的机器上,我目前有大约350个进程,循环需要近35秒的时间,每个进程大约需要0.1秒的时间。即使循环只包含$null = $obj.$v,它也需要这么长的时间。 - René Nyffenegger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接