Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
此Powershell代码段的哪一部分导致运行时间过长?_Powershell_Sorting_Search_Active Directory_Execution Time - Fatal编程技术网

此Powershell代码段的哪一部分导致运行时间过长?

此Powershell代码段的哪一部分导致运行时间过长?,powershell,sorting,search,active-directory,execution-time,Powershell,Sorting,Search,Active Directory,Execution Time,我的任务是为我们的AD env中的每个用户制作一份上次登录时间的报告,我显然首先向谷歌妈妈询问了一些我可以重新调整用途的东西,但找不到任何可以检查多个域控制器并协调最后一个域控制器的东西,然后吐出是否超过了任意设置的日期/天数 代码如下: foreach ($user in $usernames) { $percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100) Write

我的任务是为我们的AD env中的每个用户制作一份上次登录时间的报告,我显然首先向谷歌妈妈询问了一些我可以重新调整用途的东西,但找不到任何可以检查多个域控制器并协调最后一个域控制器的东西,然后吐出是否超过了任意设置的日期/天数

代码如下:

foreach ($user in $usernames) {
    $percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100)
    Write-Progress -Id 3 -Activity "Finding Inactive Accounts" -Status "$($percentCmpUser)% Complete:" -PercentComplete $percentCmpUser
    $allLogons = $AllUsers | Where-Object {$_.SamAccountName -match $user}
    $finalLogon = $allLogons| Sort-Object LastLogon -Descending |Select-Object -First 1
    if ($finalLogon.LastLogon -lt $time.ToFileTime()) {
        $inactiveAccounts += $finalLogon
    } 
}
$usernames是大约6000个用户名的列表

$AllUsers是一个包含18000个用户的列表,其中包括我希望在最终报告中访问的10个不同属性。我得到它的方法是,在我关心的特定OU中,为所有用户点击我们20个左右的DC中的三个。最后的脚本实际上是6k*20BEC,我确实需要点击每个DC以确保不会错过任何用户的登录

下面是$time的计算方法:

$DaysInactive = 60
$todayDate = Get-Date
$time = ($todayDate).Adddays(-($DaysInactive))
每个变量都在脚本中的其他地方使用,这就是为什么我这样将其分解

在您建议LastLogonTimestamp之前,有人告诉我它不够最新,当我询问将复制时间更改为更新时,有人告诉我不,不会发生

搜索账户似乎也不能提供非活动用户的准确视图

我愿意听取所有关于如何使这个特定代码段运行得更快,或者如何使用不同的方法以快速实现相同结果的建议


到目前为止,为特定OU中的所有用户点击每个DC大约需要10-20秒/DC,然后上面的代码片段需要30-40分钟。

有两件事很突出,但这里最大的性能杀手可能是以下两种说法:

$percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100)
# and
$allLogons = $AllUsers | Where-Object {$_.SamAccountName -match $user}
。。。这两个语句都将显示^2或二次性能特征-即,每次输入大小翻倍,所用时间将翻两番

Array.IndexOf实际上是一个循环 让我们看一下第一个:

$percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100)
这可能不是不言而喻的,但此方法调用:$usernames.IndexOf可能需要在每次执行时遍历整个$usernames列表-当您到达最后一个$user时,它需要遍历并比较$user所有6000项

有两种方法可以解决此问题:

使用常规for循环: 完全停止输出进度 写入进度非常慢-即使调用方抑制进度输出,例如$ProgressPreference='SilentlyContinue',使用进度流仍然会带来开销,尤其是在每次循环迭代中调用时

完全删除写入进度将删除计算百分比的要求:

如果您仍然需要输出进度信息,您可以通过偶尔调用Write progress来减少一些开销,例如每100次迭代调用一次:

for($i = 0; $i -lt $usernames.Count; $i++) {
    $user = $usernames[$i]
    if($i % 100 -eq 0){
        $percent = ($i / $usernames.Count) * 100
        Write-Progress -Id 3 -Activity "Finding Inactive Accounts" -PercentComplete $percent
    }
    # ...
}
... |其中,对象也只是一个循环 现在来看第二个:

$allLogons = $AllUsers | Where-Object {$_.SamAccountName -match $user}
。。。6000次,powershell必须枚举$AllUsers中的所有18000个对象,并测试它们的Where对象筛选器

<>而不是使用数组和对象,考虑将所有用户加载到一个哈希表:

# Only need to run this once, before the loop
$AllLogonsTable = @{}
$AllUsers |ForEach-Object {
    # Check if the hashtable already contains an item associated with the user name
    if(-not $AllLogonsTable.ContainsKey($_.SamAccountName)){
        # Before adding the first item, create an array we can add subsequent items to
        $AllLogonsTable[$_.SamAccountName] = @()
    }

    # Add the item to the array associated with the username
    $AllUsersTable[$_.SamAccountName] += $_
}

foreach($user in $users){
    # This will be _much faster_ than $AllUsers |Where-Object ...
    $allLogons = $AllLogonsTable[$user]
}

哈希表具有惊人的快速查找功能—通过键查找对象比使用数组上的Where对象要快得多。

有两件事非常突出,但这里最大的性能杀手可能是以下两条语句:

$percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100)
# and
$allLogons = $AllUsers | Where-Object {$_.SamAccountName -match $user}
。。。这两个语句都将显示^2或二次性能特征-即,每次输入大小翻倍,所用时间将翻两番

Array.IndexOf实际上是一个循环 让我们看一下第一个:

$percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100)
这可能不是不言而喻的,但此方法调用:$usernames.IndexOf可能需要在每次执行时遍历整个$usernames列表-当您到达最后一个$user时,它需要遍历并比较$user所有6000项

有两种方法可以解决此问题:

使用常规for循环: 完全停止输出进度 写入进度非常慢-即使调用方抑制进度输出,例如$ProgressPreference='SilentlyContinue',使用进度流仍然会带来开销,尤其是在每次循环迭代中调用时

完全删除写入进度将删除计算百分比的要求:

如果您仍然需要输出进度信息,您可以通过偶尔调用Write progress来减少一些开销,例如每100次迭代调用一次:

for($i = 0; $i -lt $usernames.Count; $i++) {
    $user = $usernames[$i]
    if($i % 100 -eq 0){
        $percent = ($i / $usernames.Count) * 100
        Write-Progress -Id 3 -Activity "Finding Inactive Accounts" -PercentComplete $percent
    }
    # ...
}
... |其中,对象也只是一个循环 现在来看第二个:

$allLogons = $AllUsers | Where-Object {$_.SamAccountName -match $user}
。。。6000次,powershell必须枚举$AllUsers中的所有18000个对象,并测试它们的Where对象筛选器

<>而不是使用数组和对象,考虑将所有用户加载到一个哈希表:

# Only need to run this once, before the loop
$AllLogonsTable = @{}
$AllUsers |ForEach-Object {
    # Check if the hashtable already contains an item associated with the user name
    if(-not $AllLogonsTable.ContainsKey($_.SamAccountName)){
        # Before adding the first item, create an array we can add subsequent items to
        $AllLogonsTable[$_.SamAccountName] = @()
    }

    # Add the item to the array associated with the username
    $AllUsersTable[$_.SamAccountName] += $_
}

foreach($user in $users){
    # This will be _much faster_ than $AllUsers |Where-Object ...
    $allLogons = $AllLogonsTable[$user]
}

哈希表具有惊人的快速查找功能-通过键查找对象比使用数组上的Where对象快得多。

我没有哈希表方面的经验,因此请原谅我这样做没有意义:如果没有。。。代码基本上将所有相同的用户名分组在一起?否则它将在表中创建一个新条目
? 也谢谢!当我从DCs获取用户时,创建哈希表是否有意义?或者这比按你的方式做会带来额外的成本吗?@YizBorol是的,如果不是的话。。。语句只确保创建一个与用户名关联的新数组。您可以预先创建哈希表,然后让每个特定于DC的结果集直接添加到哈希表中,但是微观优化这一部分本身可能不值得记住,它只需要执行一次。如果您需要更多帮助,请向我们展示您填充$AllUsers的代码:谢谢!使用hashtable减少了30-40分钟,现在大约需要1-2分钟才能完成非活动用户的查找!我只使用了write progress,因为它花费了很长时间,我想确保它不会卡在某个地方,所以我现在将其删除。我会自己处理$allusers哈希表,如果我搞不懂,我会提出一个新问题。再次感谢你,我要复习一下哈希表,它们很神奇@伊兹博洛尔他们真的很神奇!这对我来说是一件很常见的事情,我最终写了一篇文章,你可能也想看看:我没有使用哈希表的经验,所以如果这没有意义,请原谅我:如果没有。。。代码基本上将所有相同的用户名分组在一起?否则它将在表中创建一个新条目?也谢谢!当我从DCs获取用户时,创建哈希表是否有意义?或者这比按你的方式做会带来额外的成本吗?@YizBorol是的,如果不是的话。。。语句只确保创建一个与用户名关联的新数组。您可以预先创建哈希表,然后让每个特定于DC的结果集直接添加到哈希表中,但是微观优化这一部分本身可能不值得记住,它只需要执行一次。如果您需要更多帮助,请向我们展示您填充$AllUsers的代码:谢谢!使用hashtable减少了30-40分钟,现在大约需要1-2分钟才能完成非活动用户的查找!我只使用了write progress,因为它花费了很长时间,我想确保它不会卡在某个地方,所以我现在将其删除。我会自己处理$allusers哈希表,如果我搞不懂,我会提出一个新问题。再次感谢你,我要复习一下哈希表,它们很神奇@伊兹博洛尔他们真的很神奇!这对我来说是一件很平常的事,以至于我最终写了一篇文章,你可能也想看看: