Multithreading Foreach对象比Foreach-parallel更快?
我刚刚开始使用powershell,我想知道为什么我的并行srcipt比我的普通foreach对象脚本慢 我的普通foreachobject脚本:Multithreading Foreach对象比Foreach-parallel更快?,multithreading,powershell,foreach,parallel-processing,Multithreading,Powershell,Foreach,Parallel Processing,我刚刚开始使用powershell,我想知道为什么我的并行srcipt比我的普通foreach对象脚本慢 我的普通foreachobject脚本: function Get-ADUsers { #get all users in nested groups } function Get-NestedGroupUsers { param ( [Parameter(Mandatory = $true)][String]$FileName, [Param
function Get-ADUsers { #get all users in nested groups }
function Get-NestedGroupUsers {
param (
[Parameter(Mandatory = $true)][String]$FileName,
[Parameter(Mandatory = $true)][String]$searchFileURL
)
$storageHolder = @()
# $storageHolder | Export-Csv -Path "C:\Users\demandx\Desktop\AD User Lists\$FileName.csv" -NoTypeInformation -Force
$groupList = Get-Content $searchFileURL
$groupList | ForEach-Object {
$allusers = Get-ADUsers -GroupName $_
$storageHolder += $allusers
}
$storageHolder | select ParentGroup, Name, EmployeeNumber, Enabled, LastLogonDate, PasswordLastSet |Export-Csv -Path "C:\Users\demandx\Desktop\$FileName.csv" -NoTypeInformation -Force
}
我的foreach-parallel脚本(我将函数存储在psm1中,然后在这里导入。)
脚本或my get adusers(获取嵌套组中的所有用户)
平行结果
-------------------------------------------
Days : 0
Hours : 0
Minutes : 1
Seconds : 2
Milliseconds : 283
Ticks : 622833415
TotalDays : 0.000720872008101852
TotalHours : 0.0173009281944444
TotalMinutes : 1.03805569166667
TotalSeconds : 62.2833415
TotalMilliseconds : 62283.3415
非平行结果
-------------------------------------------
Days : 0
Hours : 0
Minutes : 0
Seconds : 35
Milliseconds : 322
Ticks : 353221537
TotalDays : 0.00040882122337963
TotalHours : 0.00981170936111111
TotalMinutes : 0.588702561666667
TotalSeconds : 35.3221537
TotalMilliseconds : 35322.1537
TLDR:
原因有三:
ForEach Object-Parallel
性能,脚本块的处理时间需要明显大于设置线程和环境的时间导入模块将引入开销
ForEach对象-并行
运行方式与正常的ForEach对象
运行方式非常不同
首先,一个普通的ForEach对象运行在您当前的PowerShell线程中,可以访问所有变量、加载的内存和管道。这对于我们运行的所有作业中的98%都很好,1秒的执行时间也可以。在2%的情况下,我们有一个超级CPU密集型进程,它可以最大限度地利用单个CPU核心并永远运行,或者我们需要等待其他执行时的响应(例如API请求),那么我们需要考虑的是-Parallel
并行执行背后的理念是利用您全新的AMD Ryzen™ 螺纹裂土器™ 3990X,64核/128线程,并将进程拆分为单独的“作业”,可同时跨多个CPU核和多个线程运行。这可能会将您的速度提高几个数量级,例如,可能快128倍
为了实现这一点,ForEach Object-Parallel
为您执行的每个脚本块创建一个新的“作业”,并开始将作业分散到CPU内核上执行。当您有长时间运行的CPU限制的进程时,这是非常好的,但是当您有非常短和小的作业时,您会遇到并行执行的关键,在并行执行中,设置比实际执行花费更多的时间ForEach Object-Parallel
必须为您运行的每个“作业”完全设置环境,例如,它必须为要运行的每个作业启动多个新线程和多个新PowerShell实例
为了说明所需的设置时间,如果我们向当前线程写入一次“Hello World”,则需要1毫秒:
PS C:\> Measure-Command { Write-Host "Hello World" }
Hello World
Seconds : 0
Milliseconds : 1
TotalMilliseconds : 1.9798
并行运行一个“Hello World”需要26毫秒:
PS C:\> Measure-Command { 1 | ForEach-Object -Parallel { Write-Host "Hello World" } }
Hello World
Seconds : 0
Milliseconds : 26
TotalMilliseconds : 26.052
这意味着它花了大约25毫秒来构建一个新线程,并设置环境和1毫秒的实际工作
在当前运行的线程上写入100次大约需要83毫秒:
PS C:\> Measure-Command { 1..100 | ForEach-Object { Write-Host "Hello World" } }
Hello World
...
Hello World
Hello World
Seconds : 0
Milliseconds : 83
TotalMilliseconds : 83.1846
使用-ThrottleLimit 5运行-Parallel
需要294ms:
PS C:\> Measure-Command { 1..100 | ForEach-Object -Parallel { Write-Host "Hello World" } -ThrottleLimit 5 }
Hello World
...
Hello World
Hello World
Seconds : 0
Milliseconds : 294
TotalMilliseconds : 294.3205
这说明并行运行对于微小的单个操作是多么有害。但另一方面,如果你有需要1秒运行的东西,你可以开始看看它是如何更好地工作的:
e、 g.运行5个进程,每个进程耗时1秒。首先在单个线程上:
PS C:\> Measure-Command { 1..5 | ForEach-Object { Start-Sleep -Seconds 1 } }
Seconds : 5
Milliseconds : 46
TotalSeconds : 5.046348
TotalMilliseconds : 5046.348
正如预期的那样,这只需要5秒多。现在,同时:
PS C:\> Measure-Command { 1..5 | ForEach-Object -Parallel { Start-Sleep -Seconds 1 } -ThrottleLimit 5 }
Seconds : 1
Milliseconds : 73
TotalSeconds : 1.0732423
TotalMilliseconds : 1073.2423
它只需一秒钟就完成了。如果处理时间远远超过设置时间,则-Parallel
非常有用
此外,在您的情况下,不仅您有额外的设置时间开销,而且加载一个模块(需要设置新环境)会为ForEach Object-Parallel
版本增加更多的时间
例如,让我们将模块AzureAD
导入到我们的ForEach对象中
脚本5次:
PS C:\> Measure-Command { 1..5 | ForEach-Object { Import-Module AzureAD } }
Seconds : 0
Milliseconds : 18
TotalSeconds : 0.0185406
TotalMilliseconds : 18.5406
现在使用ForEach对象-Parallel的
PS C:\> Measure-Command { 1..5 | ForEach-Object -Parallel { Import-Module AzureAD } -ThrottleLimit 5 }
Seconds : 0
Milliseconds : 125
TotalSeconds : 0.1256923
TotalMilliseconds : 125.6923
我们可以看到有一个显著的区别,因为它必须加载模块5次,而不是在线程中只加载一次,然后注意到它仍然被加载,而不是重新加载。有些进程根本不适合并行运行。有些并行化方法有很大的开销。您似乎遇到了一个或多个这样的问题。导入模块会有开销。您在每个作用域中执行此操作,而不是在单个线程中执行一次,除非您对已加载的数据执行大量CPU处理,否则多线程的开销通常会较慢。@DougMaurer不幸的是,此开销无法避免(目前),请参阅,非常感谢!这本书对我理解事物有很大帮助。
PS C:\> Measure-Command { 1..5 | ForEach-Object { Import-Module AzureAD } }
Seconds : 0
Milliseconds : 18
TotalSeconds : 0.0185406
TotalMilliseconds : 18.5406
PS C:\> Measure-Command { 1..5 | ForEach-Object -Parallel { Import-Module AzureAD } -ThrottleLimit 5 }
Seconds : 0
Milliseconds : 125
TotalSeconds : 0.1256923
TotalMilliseconds : 125.6923