Filtro de diferença de desempenho do PowerShell x função

11

Atualmente, estou lendo o livro Passo a Passo do Windows PowerShell 3.0 para obter mais informações sobre o PowerShell.

Na página 201, o autor demonstra que um filtro é mais rápido que a função com a mesma funcionalidade.

Este script leva 2,6 segundos em seu computador:

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

Measure-Command { 1..50000 | addOne }

e este 4,6 segundos

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

Measure-Command { 1..50000 | addOne }

Se eu executar esse código é obter exatamente o oposto do seu resultado:

.\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

Alguém pode me explicar isso?

Marcel Janus
fonte

Respostas:

13

A menos que o autor desse mais evidências, talvez ele estivesse cheio de ar quente. Você fez o teste e obteve o resultado e provou que ele estava errado.

Edit: Do blog de Jeffrey Snover:

Um filtro é uma função que apenas possui um scriptblock de processo

Isso por si só não é suficiente para me convencer de que um filtro terá uma vantagem de velocidade em relação a uma função, dado que ambos têm blocos de processo idênticos.

Além disso, que tipo de equipamento dos anos 50 é aquele cara que leva 4,6 segundos para adicionar um a um número?

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 segundos é golpe. Talvez o autor estivesse usando algum tipo de versão CTP do Powershell antes dos binários serem gerados. : P

Por fim, tente seu teste em uma nova sessão do PowerShell, mas na ordem inversa. Experimente a Função primeiro e o Filtro depois, ou vice-versa:

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

Vejo? O primeiro que você executar sempre será mais lento. Foi sobre o interior do .NET já ter carregado coisas na memória que agiliza a segunda operação, independentemente de ser uma função ou um filtro.

Admito, porém, que a Função ainda parece ser consistentemente mais rápida que o Filtro, independentemente de quantas vezes ela seja executada.

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

Então o autor estava errado ... e agora não me sinto mal por nunca ter usado um filtro em vez de uma função antes.

Ryan Ries
fonte
4

Na verdade, a diferença é muito menor se você usar o mesmo $ _ nos dois testes. Não investiguei a causa, mas acho que é porque o autor não está usando a mesma abordagem nos dois testes. Além disso, a saída do console pode interferir nos resultados. Se você cortar essas peças, os números serão muito semelhantes. Vejo:

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

Os resultados serão muito próximos, mesmo se você alterar a ordem dos comandos.

First

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

A documentação também diz que os filtros são basicamente atalhos para funções apenas com o bloco do processo. As funções, a menos que especificadas com um bloco de processo (ou alguma outra técnica como o uso de variáveis ​​automáticas como $ input), são executadas uma vez, não usam input e não passam para o próximo comando no pipeline.

Mais informações em https://technet.microsoft.com/en-us/library/hh847829.aspx e https://technet.microsoft.com/en-us/library/hh847781.aspx

Vinicius Xavier
fonte