Несколько вещей выделяются, но, вероятно, самый большой убийца производительности здесь - это два утверждения:
$percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100)
# and
$allLogons = $AllUsers | Where-Object {$_.SamAccountName -match $user}
... оба эти утверждения будут иметь O (N ^ 2) (или quadrati c) эксплуатационные характеристики - то есть каждый раз, когда вы удваиваете размер ввода, время, затрачиваемое в четыре раза!
Array.IndexOf()
фактически является oop
Давайте посмотрим на первый:
$percentCmpUser = [math]::Truncate(($usernames.IndexOf($user)/$usernames.count)*100)
Возможно, не быть самоочевидным, но этот вызов метода: $usernames.IndexOf()
может потребовать итерации по всему списку $usernames
каждый раз, когда он выполняется - к тому времени, когда вы достигнете последнего $user
, ему нужно go до конца и сравнить $user
всех 6000 наименований.
Два способа решения этой проблемы:
Используйте обычные for
l oop:
for($i = 0; $i -lt $usernames.Count; $i++) {
$user = $usernames[$i]
$percent = ($i / $usernames.Count) * 100
# ...
}
Остановить вывод прогресса вообще
Write-Progress
очень медленно - даже если вызывающая сторона подавляет вывод Progress (например, $ProgressPreference = 'SilentlyContinue'
), usi При этом поток выполнения по-прежнему несет издержки, особенно при вызове в каждой итерации l oop.
Удаление Write-Progress
в целом приведет к удалению требования для расчета процента:)
Если вам все еще нужно выводя информацию о прогрессе, вы можете уменьшить некоторые накладные расходы, просто вызвав 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
}
# ...
}
... |Where-Object
это также просто al oop
Теперь для второго:
$allLogons = $AllUsers | Where-Object {$_.SamAccountName -match $user}
. .. 6000 раз, powershell должен перечислить все 18000 объектов в $AllUsers
и проверить их на наличие фильтра Where-Object
.
Вместо использования массива и Where-Object
рассмотрите возможность загрузки всех пользователей в хеш-таблицу:
# 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]
}
Хеш-таблицы имеют crazy-fast lookups - поиск объекта по ключу намного быстрее, чем использование Where-Object
в массиве.