Scroll down for TL;DR
I need to get the following properties for every process as quickly as possible, ideally 5 seconds, maximum 10 seconds: ID, Name, Description, Path, Company, Username, Session ID, StartTime, Memory, CPU (percentage, not time)
To get this data, I put together the following snippet which (I think) is functionally perfect:
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
@{Name='Id';Expression={[int]$_.Id}},
@{Name='Name';Expression={[string]$_.Name}},
@{Name='Description';Expression={[string]$_.Description}},
@{Name='Path';Expression={[string]$_.Path}},
@{Name='Company';Expression={[string]$_.Company}},
@{Name='Username';Expression={[string]$_.UserName}},
@{Name='SessionId';Expression={[string]$_.SessionId}},
@{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
@{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
@{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
The issue is that its taking 18-22 seconds to execute, caused by this line (which adds about 16 seconds):
@{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
PS C:\Windows\system32> Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
@{Name='Id';Expression={[int]$_.Id}},
@{Name='Name';Expression={[string]$_.Name}},
@{Name='Description';Expression={[string]$_.Description}},
@{Name='Path';Expression={[string]$_.Path}},
@{Name='Company';Expression={[string]$_.Company}},
@{Name='Username';Expression={[string]$_.UserName}},
@{Name='SessionId';Expression={[string]$_.SessionId}},
@{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
@{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
@{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
}
TotalSeconds : 19.061206
When I remove the slow property expression noted above and keep the WMI query, execution takes about 4.5 seconds:
Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
@{Name='Id';Expression={[int]$_.Id}},
@{Name='Name';Expression={[string]$_.Name}},
@{Name='Description';Expression={[string]$_.Description}},
@{Name='Path';Expression={[string]$_.Path}},
@{Name='Company';Expression={[string]$_.Company}},
@{Name='Username';Expression={[string]$_.UserName}},
@{Name='SessionId';Expression={[string]$_.SessionId}},
@{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
@{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}}
}
TotalSeconds : 4.5202906
I thought that by getting all of the required data in a single query and referring back to the $ProcessCPU
array would be fast - but I appreciate I'm iterating through each of the 250 arrays stored in $Processes
.
TL;DR:
Is there a more performant method of joining two objects on a common property rather than using iteration as I have above? I.E. $ProcessCPU.IDProcess on $Processes.Id
?
I tried the following block to test $Output = $ProcessCPU + $Processes | Group-Object -Property Id
, it executed in just 3 seconds, but the output wasn't acceptable:
PS C:\Windows\system32> Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object @{Name='Id';Expression={[int]$_.IDProcess}}, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
@{Name='Id';Expression={[int]$_.Id}},
@{Name='Name';Expression={[string]$_.Name}},
@{Name='Description';Expression={[string]$_.Description}},
@{Name='Path';Expression={[string]$_.Path}},
@{Name='Company';Expression={[string]$_.Company}},
@{Name='Username';Expression={[string]$_.UserName}},
@{Name='SessionId';Expression={[string]$_.SessionId}},
@{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
@{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}}
$Output = $ProcessCPU + $Processes | Group-Object -Property Id
}
TotalSeconds : 2.9656969