PowerShell 属性 表达式将执行时间增加 4-5 倍
PowerShell property expression increases execution time by 4-5 times
向下滚动查看 TL;DR
我需要尽快获取每个进程的以下属性,最好是 5 秒,最多 10 秒:ID、名称、说明、路径、公司、用户名、会话 ID、开始时间、内存,CPU(百分比,不是时间)
为了获取这些数据,我整理了以下片段(我认为)在功能上是完美的:
$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
}}
问题是执行需要 18-22 秒,由以下行(增加了大约 16 秒)引起:
@{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
当我删除上面提到的缓慢 属性 表达式并保留 WMI 查询时,执行大约需要 4.5 秒:
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
我认为通过在单个查询中获取所有必需的数据并返回引用 $ProcessCPU
数组会很快 - 但我很感激我正在遍历存储在 $Processes
.
长话短说:
有没有比使用上面的迭代更高效的方法将两个对象连接到一个公共 属性 上? IE。 $ProcessCPU.IDProcess on $Processes.Id
?
我尝试了以下块来测试 $Output = $ProcessCPU + $Processes | Group-Object -Property Id
,它只执行了 3 秒,但输出不可接受:
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
首先使用 CIM 建立一个哈希表,将进程 ID (PID) 映射到它们的 CPU 百分比。
然后将计算的 属性 传递给 Select-Object
查阅该哈希表以进行高效查找:
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
ForEach-Object -Begin { $htCpuPctg=@{} } `
-Process { $htCpuPctg[$_.IdProcess] = $_.PercentProcessorTime } #`
Get-Process -IncludeUserName |
Select-Object Id,
Name,
Description,
Path,
Company,
UserName,
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={ $htCpuPctg[[uint32] $_.Id] }}
注:
使用 Get-CimInstance
而不是 Get-WimObject
,因为 CIM cmdlet 取代了 PowerShell v3(2012 年 9 月发布)中的 WMI cmdlet。因此,应该避免使用 WMI cmdlet,尤其是因为 PowerShell Core,所有未来的努力都将去向,甚至 没有 它们了。有关详细信息,请参阅 this answer。
通常不需要使用 @{Name='Id';Expression={[int]$_.Id}}
等计算属性来简单地按原样提取 属性 - 只需使用 属性 的 name - Id
- 作为 Select-Object -Property
参数(但你已经澄清你正在使用计算属性,因为你想明确控制 属性 的数据类型,用于通过 JSON).
向物联网网关发送数据
请注意,CIM 将 PID 报告为 [uint32]
类型的值,而 Get-Process
使用 [int]
值 - 因此需要转换为 [uint32]
在哈希表查找中。
向下滚动查看 TL;DR
我需要尽快获取每个进程的以下属性,最好是 5 秒,最多 10 秒:ID、名称、说明、路径、公司、用户名、会话 ID、开始时间、内存,CPU(百分比,不是时间)
为了获取这些数据,我整理了以下片段(我认为)在功能上是完美的:
$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
}}
问题是执行需要 18-22 秒,由以下行(增加了大约 16 秒)引起:
@{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
当我删除上面提到的缓慢 属性 表达式并保留 WMI 查询时,执行大约需要 4.5 秒:
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
我认为通过在单个查询中获取所有必需的数据并返回引用 $ProcessCPU
数组会很快 - 但我很感激我正在遍历存储在 $Processes
.
长话短说:
有没有比使用上面的迭代更高效的方法将两个对象连接到一个公共 属性 上? IE。 $ProcessCPU.IDProcess on $Processes.Id
?
我尝试了以下块来测试 $Output = $ProcessCPU + $Processes | Group-Object -Property Id
,它只执行了 3 秒,但输出不可接受:
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
首先使用 CIM 建立一个哈希表,将进程 ID (PID) 映射到它们的 CPU 百分比。
然后将计算的 属性 传递给
Select-Object
查阅该哈希表以进行高效查找:
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
ForEach-Object -Begin { $htCpuPctg=@{} } `
-Process { $htCpuPctg[$_.IdProcess] = $_.PercentProcessorTime } #`
Get-Process -IncludeUserName |
Select-Object Id,
Name,
Description,
Path,
Company,
UserName,
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={ $htCpuPctg[[uint32] $_.Id] }}
注:
-
使用
Get-CimInstance
而不是Get-WimObject
,因为 CIM cmdlet 取代了 PowerShell v3(2012 年 9 月发布)中的 WMI cmdlet。因此,应该避免使用 WMI cmdlet,尤其是因为 PowerShell Core,所有未来的努力都将去向,甚至 没有 它们了。有关详细信息,请参阅 this answer。通常不需要使用
@{Name='Id';Expression={[int]$_.Id}}
等计算属性来简单地按原样提取 属性 - 只需使用 属性 的 name -Id
- 作为Select-Object -Property
参数(但你已经澄清你正在使用计算属性,因为你想明确控制 属性 的数据类型,用于通过 JSON). 向物联网网关发送数据
请注意,CIM 将 PID 报告为
[uint32]
类型的值,而Get-Process
使用[int]
值 - 因此需要转换为[uint32]
在哈希表查找中。