#performance #powershell
#Производительность #powershell
Вопрос:
Смотрите приведенный ниже код, единственное различие в двух сценариях заключается в отсутствии блока сценария во втором, и из-за этого происходит значительное увеличение производительности на %.
Есть ли причина для этого? Является ли один из них более родным для powershell, чем другой?
Я выполняю большое количество сценариев с несколькими похожими блоками во многих из них и хотел бы получить разумный ответ о том, как легко повысить производительность, такую как эта, так почему исключение scriptblock в Where-Object
(псевдоним ?
) внезапно снижает производительность на приличную величину?
PS C:Scripts> $a = 1..15 | % {
Measure-Command {
$G = Get-ADGroup -Filter *
1..3 | % {
$G | ? {$_.Name -eq "TestGroup$($_)"}
}
}
}
$b = 1..15 | % {
Measure-Command {
$G = Get-ADGroup -Filter *
1..3 | % {
$G | ? Name -eq "TestGroup$($_)"
}
}
}
($a.TotalMilliseconds | Measure -Average).Average
($b.TotalMilliseconds | Measure -Average).Average
283.479413333333
212.57384
Комментарии:
1. На самом деле разница намного больше. Чтобы изолировать его, переместите
$G
назначение из измерения и используйте больший внутренний цикл, например1..1e5
.2. Не для придирчивости, а скорее для того, чтобы избежать путаницы: это не «снижает производительность» . Это либо «сокращает время выполнения», либо «повышает производительность». Выберите один. 🙂
Ответ №1:
В PowerShell 3.0 появились Where-Object
(псевдонимы: Where
, ?
), которые могут проверять свойства напрямую, без scriptblock.
Когда выполняется блок сценариев, PowerShell, как и любой другой язык, создает новый контекст выполнения, и это очень дорого на интерпретируемом языке.
Единственными причинами использования старой нотации с scriptblock являются:
- чтобы иметь код, совместимый с PS1 / PS2;
- для выполнения сложных проверок;
- чтобы сделать что-то помимо самой проверки.
Что касается вашего кода, оба фрагмента используют scriptblocks в других местах, конвейерную обработку (это в 5-10 раз медленнее, чем foreach
оператор) и без необходимости измеряют Get-ADGroup
время, поэтому мы можем оптимизировать его еще больше:
$group = Get-ADGroup -Filter *
$c = foreach ($i in 1..15) {
Measure-Command {
foreach ($j in 1..3) {
$filtered = foreach ($g in $group) { if ($g.Name -eq "TestGroup$($j)") { $g } }
}
}
}
Иногда гораздо большего выигрыша можно добиться путем подготовки данных. Например, если некоторый массив объектов повторно проверяется в цикле, чтобы увидеть, имеет ли он определенное значение свойства, лучше преобразовать массив в хэш-таблицу с ключом, основанным на значении этого свойства:
# index by somefield
$lookup = @{}
foreach ($element in $array) { $lookup[$element.somefield] = $element }
# use the indexed lookup table
foreach ($element in $anotherarray) {
$existingElement = $lookup[$element.somefield]
if ($existingElement) {
# do something
}
}