Производительность Powershell с Where-Object?

#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 
    }
}