PowerShell-Leistungsunterschiedsfilter vs. Funktion

11

Ich lese derzeit das Schritt-für-Schritt-Buch zu Windows PowerShell 3.0, um weitere Einblicke in PowerShell zu erhalten.

Auf Seite 201 zeigt der Autor, dass ein Filter schneller als die Funktion mit derselben Funktion ist.

Dieses Skript dauert auf seinem Computer 2,6 Sekunden:

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

Measure-Command { 1..50000 | addOne }

und dieser 4,6 Sekunden

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

Measure-Command { 1..50000 | addOne }

Wenn ich diesen Code ausführe, ist das genaue Gegenteil von seinem Ergebnis:

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

Kann mir das jemand erklären?

Marcel Janus
quelle

Antworten:

13

Wenn der Autor keine weiteren Beweise lieferte, war er vielleicht nur voller heißer Luft. Sie haben den Test durchgeführt und das Ergebnis erhalten und ihm das Gegenteil bewiesen.

Edit: Aus Jeffrey Snovers Blog:

Ein Filter ist eine Funktion, die nur einen Prozessskriptblock enthält

Das allein reicht nicht aus, um mich davon zu überzeugen, dass ein Filter einen Geschwindigkeitsvorteil gegenüber einer Funktion hat, da beide identische Prozessblöcke haben.

Was für eine Ausrüstung aus den 1950er Jahren hat dieser Typ, bei der es 4,6 Sekunden dauert, um einer Zahl eine hinzuzufügen?

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 Sekunden ist verrückt. Vielleicht hat der Autor eine Art CTP-Version von Powershell verwendet, bevor die Binärdateien erstellt wurden. : P.

Versuchen Sie schließlich Ihren Test in einer neuen Powershell-Sitzung, jedoch in umgekehrter Reihenfolge. Probieren Sie zuerst die Funktion und dann den Filter aus oder umgekehrt:

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

Sehen? Der erste, den Sie ausführen, ist immer langsamer. Es ging nur um die .NET-Interna, bereits Inhalte in den Speicher geladen zu haben, die den zweiten Vorgang beschleunigen, unabhängig davon, ob es sich um eine Funktion oder einen Filter handelt.

Ich gebe jedoch zu, dass die Funktion immer noch konstant schneller zu sein scheint als der Filter, unabhängig davon, wie oft sie ausgeführt wird.

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

Der Autor hat sich also geirrt ... und jetzt fühle ich mich nicht schlecht, weil ich noch nie zuvor einen Filter anstelle einer Funktion verwendet habe.

Ryan Ries
quelle
4

Tatsächlich ist der Unterschied viel geringer, wenn Sie in beiden Tests dasselbe $ _ verwenden. Ich habe die Ursache nicht untersucht, aber ich nehme an, dass der Autor in beiden Tests nicht denselben Ansatz verwendet. Außerdem kann die Konsolenausgabe die Ergebnisse beeinträchtigen. Wenn Sie diese Teile schneiden, sind die Zahlen sehr ähnlich. Sehen:

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

Die Ergebnisse sind sehr ähnlich, auch wenn Sie die Reihenfolge der Befehle ändern.

First

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

Die Dokumentation besagt auch, dass Filter im Grunde Verknüpfungen zu Funktionen sind, die nur den Prozessblock enthalten. Funktionen werden, sofern sie nicht mit einem Prozessblock angegeben wurden (oder einer anderen Technik wie der Verwendung automatischer Variablen wie $ input), einmal ausgeführt, verwenden keine Eingabe und werden nicht an den nächsten Befehl in der Pipeline übergeben.

Weitere Informationen unter https://technet.microsoft.com/en-us/library/hh847829.aspx und https://technet.microsoft.com/en-us/library/hh847781.aspx

Vinicius Xavier
quelle