'Is there a way to speed up dynamic member look-up in ps-objects

In the following code, most time is spent in $v = $proc.$columnName and I am wondering if there is a way to speed up looking up the objects's members's values.

In the code below, I have chosen $objs to be the result of get-process but in my case, $objs could be an array of any type of objects, thus the need to look up the objects's members dynamically.

$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"


Solution 1:[1]

Perhaps there are more ways to speed this up, but below I have taken out the unnecessary bits:

$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"

Solution 2:[2]

This is a hidden property on PSObjects called .PSObject that way too many people don't know about, including myself until a few weeks ago despite spending thousands of hours working with PowerShell. From there you can use .where to SIGNIFICANTLY increase filtering performance.

New Code Block

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"

Comparison Result

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

Validation & Comparison

#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
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Theo
Solution 2 Nick W.