【发布时间】:2018-10-19 10:31:53
【问题描述】:
我正在分析一些代码以提高性能,但没有从运行空间中获得我期望的结果。
我的源文件是 7 个 Autodesk Revit 日志文件,从 13MB 和 150K 行到 90MB 和 900K 行不等。每个文件都包含多次引用它自己的名称,所以我将这个计数作为我以后想做的一些实际工作的代理。在下面的代码中,我使用简单的 foreach 处理所有文件,然后再次使用限制为 8 的运行空间。在这两种情况下,我都使用流阅读器来解析文件,因为文件可能会比我正在测试的文件大得多和。我不希望运行空间示例是循环时间的 25%,但我当然希望它更接近 25%,而不是 50%。相反,我看到的改进不到 50%。最后一次运行是单线程的 14.26 秒和 8 个运行空间的 8.74 秒。我在代码中做错了什么,还是我的期望不正确? FWIW 我目前正在虚拟机上进行测试。我尝试为 VM 分配 4、6、8 和 12 个内核,结果差别不大。最后一个测试是分配了 12 个内核,运行空间限制为 8 个。这是在主机上使用 10 个内核超线程 Xeon。
编辑:我修改了代码以将资源文件复制到 temp,以删除网络变量,并添加了一个基于作业的测试,再次限制为运行空间被限制到的相同 8 个并发线程。时间大致为 16.8 vs 9.6 vs 7.3。因此,Jobs 一直更好,尽管我的理解是运行空间更高效并且应该更快,而且性能仍然只比 50% 的节省好不了多少,即使有 8 个线程。
$source = '\\Px\Support\Profiling\Source'
$localSource = "$env:TEMP\Px"
Clear-Host
if (Test-Path $localSource) {
Remove-Item "$localSource\*" -Recurse -force
} else {
New-Item $localSource -ItemType:Directory > $null
}
Copy-Item "$source\*" $localSource
$journals = Get-ChildItem $localSource
Write-Host "Single Thread"
(Measure-Command {
foreach ($journal in $journals) {
$count = 0
#$reader = [IO.StreamReader]::New($journal.fullName, $true)
$reader = New-Object -typeName:System.IO.StreamReader -argumentList $journal.fullName
while (-not ($reader.EndOfStream)) {
$line = ($reader.ReadLine()).Trim()
if ($line -match $journal) {
$count ++
}
}
Write-Host "$journal $count"
$reader.Close()
$reader.Dispose()
}
}).totalSeconds
Write-Host
Write-Host "Runspace 1,8"
(Measure-Command {
$runspacePool = [RunspaceFactory]::CreateRunspacePool(1,8)
$runspacePool.Open()
$runspaceCollection = New-Object system.collections.arraylist
$scriptBlock = {
param (
[string]$journal
)
$journalName = Split-Path $journal -leaf
$count = 0
#$reader = [IO.StreamReader]::New($journal, $true)
$reader = New-Object -typeName:System.IO.StreamReader -argumentList $journal
while (-not ($reader.EndOfStream)) {
$line = ($reader.ReadLine()).Trim()
if ($line -match $journalName) {
$count ++
}
}
$reader.Close()
$reader.Dispose()
"$journalName $count"
}
foreach ($journal in $journals) {
$parameters = @{
journal = $journal.fullName
}
$powershell = [PowerShell]::Create()
$powershell.RunspacePool = $RunspacePool
$powershell.AddScript($scriptBlock) > $null
$powershell.AddParameters($parameters) > $null
$runspace = New-Object -TypeName PSObject -Property @{
runspace = $powershell.BeginInvoke()
powerShell = $powershell
}
$runspaceCollection.Add($runspace) > $null
}
while($runspaceCollection){
foreach($runspace in $runspaceCollection.ToArray()){
if($runspace.RunSpace.IsCompleted -eq $true){
Write-Host "$($runspace.Powershell.EndInvoke($runspace.RunSpace))"
$runspace.Powershell.dispose()
$runspaceCollection.Remove($runspace)
#[System.GC]::Collect()
Start-Sleep -m:100
}
}
}
}).totalSeconds
Write-Host
Write-Host "Jobs 8"
Remove-Job *
(Measure-Command {
$scriptBlock = {
param (
[string]$journal
)
$journalName = Split-Path $journal -leaf
$count = 0
#$reader = [IO.StreamReader]::New($journal, $true)
$reader = New-Object -typeName:System.IO.StreamReader -argumentList:$journal
while (-not ($reader.EndOfStream)) {
$line = ($reader.ReadLine()).Trim()
if ($line -match $journalName) {
$count ++
}
}
$reader.Close()
$reader.Dispose()
Write-Output "$journalName $count"
}
foreach ($journal in $journals) {
Start-Job -ScriptBlock:$scriptBlock -argumentlist:$journal.fullName
While($(Get-Job -State 'Running').Count -ge 8) {
sleep -m:100
}
}
Get-Job | Wait-Job
foreach ($job in Get-Job) {
Write-Host "$(Receive-Job $job)"
Remove-Job $job
}
}).totalSeconds
Write-Host
Remove-Item $localSource -Recurse -force
【问题讨论】:
-
您是否忘记从 runespace 版本中排除
Start-Sleep -m:100部分? -
@kirill Start-Sleep 只是为了减少查询集合的开销。我试过没有,实际上更糟。非常有趣的是,我现在正在使用 Windows 10(VM 运行 Windows 7)的 Bootcamp 中对其进行测试,而在 Bootcamp 中几乎没有性能差异。我知道Win10很臃肿,但是老天,这对微软来说太尴尬了。
-
问题 - 如果您只读取 7 个文件,为什么要使用 8 个线程?我看到其他人指出要使用的最佳最大线程数是 ProcessorCount+1。不要指望它本身会帮助解决问题,但可能有助于微调脚本。
标签: multithreading powershell runspace