Filtro differenza di prestazioni PowerShell vs. funzione


11

Attualmente sto leggendo il libro Step by Step di Windows PowerShell 3.0 per ottenere ulteriori informazioni su PowerShell.

Nella pagina 201 l'autore dimostra che un filtro è più veloce della funzione con lo stesso funzionalmente.

Questo script richiede 2,6 secondi sul suo computer:

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

Measure-Command { 1..50000 | addOne }

e questo 4.6 secondi

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

Measure-Command { 1..50000 | addOne }

Se eseguo questo codice è ottenere l'esatto contrario del suo risultato:

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

Qualcuno può spiegarmelo?

Risposte:


13

A meno che l'autore non abbia fornito ulteriori prove a sostegno, forse era solo pieno di aria calda. Hai eseguito il test, ottenuto il risultato e dimostrato di aver sbagliato.

Modifica: dal blog di Jeffrey Snover:

Un filtro è una funzione che ha solo un blocco script di processo

Questo da solo non è abbastanza per convincermi che un filtro avrà un vantaggio di velocità rispetto a una funzione, dato che entrambi hanno blocchi di processo identici.

Inoltre, che tipo di equipaggiamento degli anni '50 è quel tizio in cui ci vogliono 4,6 secondi per aggiungerne uno a un numero?

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 secondi è sbalorditivo. Forse l'autore stava usando una sorta di versione CTP di Powershell prima che i binari fossero modificati. : P

Infine, prova il tuo test in una nuova sessione di Powershell, ma in ordine inverso. Prova prima la funzione e poi il filtro, o viceversa:

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

Vedere? Il primo che corri sarà sempre più lento. Era proprio a proposito degli interni .NET di aver già caricato elementi in memoria che rende più veloce la seconda operazione, indipendentemente dal fatto che si tratti di una funzione o di un filtro.

Devo ammettere però che la Funzione sembra essere sempre più veloce del Filtro, indipendentemente da quante volte viene eseguita.

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

Quindi l'autore aveva torto ... e ora non mi sento male per non aver mai usato prima un Filtro anziché una Funzione.


4

In realtà la differenza è molto più piccola se si usano gli stessi $ _ in entrambi i test. Non ho studiato la causa, ma suppongo sia perché l'autore non sta usando lo stesso approccio in entrambi i test. Inoltre, l'output della console può interferire nei risultati. Se tagli queste parti, i numeri sono molto simili. Vedere:

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

I risultati saranno molto vicini, anche se si cambia l'ordine dei comandi.

First

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

La documentazione afferma inoltre che i filtri sono sostanzialmente collegamenti a funzioni con solo il blocco di processo. Le funzioni, se non specificate con un blocco di processo (o qualche altra tecnica come l'uso di variabili automatiche come $ input), vengono eseguite una volta, non utilizzano l'input e non passano al comando successivo nella pipeline.

Maggiori informazioni su https://technet.microsoft.com/en-us/library/hh847829.aspx e https://technet.microsoft.com/en-us/library/hh847781.aspx

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.