Filtre de différence de performances PowerShell par rapport à la fonction

11

Je lis actuellement le livre étape par étape de Windows PowerShell 3.0 pour obtenir plus d'informations sur PowerShell.

À la page 201, l'auteur montre qu'un filtre est plus rapide que la fonction avec le même fonctionnellement.

Ce script prend 2,6 secondes sur son ordinateur:

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

Measure-Command { 1..50000 | addOne }

et celui-ci 4,6 secondes

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

Measure-Command { 1..50000 | addOne }

Si je lance ce code, c'est exactement le contraire de son résultat:

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

Quelqu'un peut m'expliquer cela?

Marcel Janus
la source

Réponses:

13

À moins que l'auteur ne fournisse plus de preuves à l'appui, il était peut-être juste plein d'air chaud. Vous avez effectué le test et obtenu le résultat et vous lui avez donné tort.

Edit: Du blog de Jeffrey Snover:

Un filtre est une fonction qui a juste un bloc de script de processus

Cela seul ne suffit pas à me convaincre qu'un filtre va avoir un avantage de vitesse sur une fonction, étant donné que les deux ont des blocs de processus identiques.

De plus, quel type d'équipement des années 1950 est ce gars là où il faut 4,6 secondes pour ajouter un à un numéro?

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 secondes est détraqué. Peut-être que l'auteur utilisait une sorte de version CTP de Powershell avant la génération des binaires. : P

Enfin, essayez votre test dans une nouvelle session Powershell, mais dans l'ordre inverse. Essayez la fonction en premier et le filtre en second, 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

Voir? Le premier que vous exécutez sera toujours plus lent. C'était à peu près les éléments internes .NET d'avoir déjà chargé des choses en mémoire qui rend la deuxième opération plus rapide, qu'il s'agisse d'une fonction ou d'un filtre.

Je dois admettre cependant que la fonction semble toujours être plus rapide que le filtre, quel que soit le nombre de fois où elle est exécutée.

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

Donc, l'auteur avait tort ... et maintenant je ne me sens pas mal de n'avoir jamais utilisé un filtre au lieu d'une fonction auparavant.

Ryan Ries
la source
4

En fait, la différence est beaucoup plus petite si vous utilisez le même $ _ dans les deux tests. Je n'ai pas enquêté sur la cause, mais je suppose que c'est parce que l'auteur n'utilise pas la même approche dans les deux tests. De plus, la sortie de la console peut interférer dans les résultats. Si vous coupez ces pièces, les chiffres sont très similaires. Voir:

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

Les résultats seront très proches, même si vous modifiez l'ordre des commandes.

First

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

La documentation indique également que les filtres sont essentiellement des raccourcis vers des fonctions avec uniquement le bloc de processus. Les fonctions, sauf si elles sont spécifiées avec un bloc de processus (ou une autre technique comme l'utilisation de variables automatiques telles que $ input), s'exécutent une fois, n'utilisent pas d'entrée et ne passent pas à la commande suivante dans le pipeline.

Plus d'informations sur https://technet.microsoft.com/en-us/library/hh847829.aspx et https://technet.microsoft.com/en-us/library/hh847781.aspx

Vinicius Xavier
la source