Фильтр разницы производительности PowerShell и функция

11

В настоящее время я читаю книгу «Шаг за шагом» по Windows PowerShell 3.0, чтобы узнать больше о PowerShell.

На странице 201 автор демонстрирует, что фильтр работает быстрее, чем функция с той же функциональностью.

Этот скрипт занимает 2,6 секунды на своем компьютере:

MeasureAddOneFilter.ps1
Filter AddOne
{ 
 "add one filter"
  $_ + 1
}

Measure-Command { 1..50000 | addOne }

и этот 4,6 секунды

MeasureAddOneFunction.ps1
Function AddOne
{  
  "Add One Function"
  While ($input.moveNext())
   {
     $input.current + 1
   }
}

Measure-Command { 1..50000 | addOne }

Если я запускаю этот код, получаю полную противоположность его результату:

.\MeasureAddOneFilter.ps1
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 226
Ticks             : 2266171
TotalDays         : 2,62288310185185E-06
TotalHours        : 6,29491944444444E-05
TotalMinutes      : 0,00377695166666667
TotalSeconds      : 0,2266171
TotalMilliseconds : 226,6171

.\MeasureAddOneFunction.ps1

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 93
Ticks             : 933649
TotalDays         : 1,08061226851852E-06
TotalHours        : 2,59346944444444E-05
TotalMinutes      : 0,00155608166666667
TotalSeconds      : 0,0933649
TotalMilliseconds : 93,3649

Может кто-то объяснить это мне?

Марсель Янус
источник

Ответы:

13

Если автор не предоставил больше подтверждающих доказательств, возможно, он был просто полон горячего воздуха. Вы провели тест, получили результат и доказали, что он не прав.

Редактировать: Из блога Джеффри Сновера:

Фильтр - это функция, которая имеет только блок сценария процесса.

Одного этого недостаточно, чтобы убедить меня в том, что фильтр будет иметь преимущество в скорости перед функцией, учитывая, что оба имеют идентичные блоки процесса.

Кроме того, что за оборудование 1950-х годов у того парня, где требуется 4,6 секунды, чтобы добавить его к числу?

PS C:\Users\Ryan> Measure-Command { Filter AddOne { $_ + 1 }; AddOne 1 }

TotalMilliseconds : 7.7266


PS C:\Users\Ryan> Measure-Command { Function AddOne { $_ + 1 }; AddOne 1 }    

TotalMilliseconds : 0.4108

4,6 секунды Возможно, автор использовал некую CTP-версию Powershell до того, как исполняемые файлы были созданы. :П

Наконец, попробуйте выполнить тест в новом сеансе Powershell, но в обратном порядке. Попробуйте сначала функцию, а затем фильтр, или наоборот:

PS C:\Users\Ryan> Measure-Command { Function AddOne { $_ + 1 }; AddOne 1 }    

TotalMilliseconds : 6.597    


PS C:\Users\Ryan> Measure-Command { Filter AddOne { $_ + 1 }; AddOne 1 }

TotalMilliseconds : 0.4055

Увидеть? Первый, который вы запускаете, всегда будет медленнее. Речь шла о внутренних компонентах .NET, которые уже загружали вещи в память, что ускоряет вторую операцию, независимо от того, является ли она функцией или фильтром.

Однако я признаю, что функция по-прежнему работает быстрее, чем фильтр, независимо от того, сколько раз он запускается.

Measure-Command { Function AddOne($Num) { Return $Num += 1 }; 1..50000 | AddOne $_ }

TotalMilliseconds : 13.9813

Measure-Command { Filter AddOne($Num) { Return $Num += 1 }; 1..50000 | AddOne $_ }

TotalMilliseconds : 69.5301

Так что автор был неправ ... и теперь я не расстраиваюсь из-за того, что никогда раньше не использовал Фильтр вместо Функции.

Райан Райс
источник
4

На самом деле разница намного меньше, если вы используете один и тот же $ _ в обоих тестах. Я не исследовал причину, но я предполагаю, что это потому, что автор не использует один и тот же подход в обоих тестах. Кроме того, консольный вывод может повлиять на результаты. Если вырезать эти части, цифры очень похожи. Увидеть:

Function AddOneFunction
{  
    process {
        $_ + 1
    }
}

Filter AddOneFilter
{ 
    $_ + 1
}

write-host "First"
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFilter } | select totalMilliseconds

write-host "Second"
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds
Measure-Command { 1..50000 | AddOneFunction } | select totalMilliseconds

Результаты будут очень близки, даже если вы измените порядок команд.

First

TotalMilliseconds
-----------------
        84.6742
        84.7646
        89.8603
        82.3399
        83.8195
Second
        86.8978
        87.4064
        89.304
        94.4334
        87.0135

В документации также сказано, что Фильтры - это в основном ярлыки для функций только с блоком процесса. Функции, если они не указаны с помощью блока процесса (или другого метода, такого как использование автоматических переменных, таких как $ input), выполняются один раз, не используют ввод и не передаются следующей команде в конвейере.

Более подробная информация на https://technet.microsoft.com/en-us/library/hh847829.aspx и https://technet.microsoft.com/en-us/library/hh847781.aspx

Виниций Ксавье
источник