Timing der Ausführung eines Befehls in PowerShell

217

Gibt es eine einfache Möglichkeit, die Ausführung eines Befehls in PowerShell zeitlich zu steuern, wie den Befehl 'time' unter Linux?
Ich habe mir das ausgedacht:

$s=Get-Date; .\do_something.ps1 ; $e=Get-Date; ($e - $s).TotalSeconds

Aber ich hätte gerne etwas Einfacheres

time .\do_something.ps1
Paolo Tedesco
quelle

Antworten:

337

Jep.

Measure-Command { .\do_something.ps1 }

Beachten Sie, dass ein kleiner Nachteil darin Measure-Commandbesteht, dass Sie keine stdoutAusgabe sehen.

[Update, dank @JasonMArcher] Sie können das Problem beheben , indem Sie die Befehlsausgabe zu einem gewissen Commandlet kochend , dass Schreiben an den Host, zum Beispiel Out-Defaultso wird es:

Measure-Command { .\do_something.ps1 | Out-Default }

Eine andere Möglichkeit, die Ausgabe anzuzeigen, besteht darin, die .NET- StopwatchKlasse wie folgt zu verwenden:

$sw = [Diagnostics.Stopwatch]::StartNew()
.\do_something.ps1
$sw.Stop()
$sw.Elapsed
Keith Hill
quelle
114
Sie können auch die Ausgabe wie folgt sehen: Measure-Command {ps | Out-Default}. Oder irgendetwas anderes, das direkt auf den Host schreibt, was nützlich sein kann oder nicht.
JasonMArcher
18
Ich nahm diese Lösung und schrieb eine Funktion, die für andere nützlich sein kann. gist.github.com/2206444 - Beispiel: Führttime { ping -n 1 google.com } -Samples 10 die Befehlszeiten aus 10und gibt die durchschnittliche, minimale und maximale Zeit zurück. Sie können hinzufügen, -Silentum STDOUT zu schlucken.
Joshuapoehls
13
Ich würde es vorziehen, das Ergebnis von Measure-Command einer Variablen zuzuweisen, wie z $t = Measure-Command {<<your command or code block>>}. Probieren Sie es aus und geben Sie dann $tan der Eingabeaufforderung Ihre Ergebnisse zu sehen und alle Eigenschaften , die Sie Zugriff haben, wie $t.Milliseconds, $t.TotalSecondsusw. Dann können wir schreiben , was auch immer Ausgabe wollen wir zum BeispielWrite-Host That command took $t.TotalSeconds to complete.
Baodad
Was ist schneller zu bedienen? net.stopwatch oder Measure-Command oder einfach nur zwei Get-Date-Variablen vergleichen ... (Ich meine, was ist effizienter, um dauerhaft in einem Skript zu bleiben?)
Hicsy
Fügen Sie vielleicht den Kern von JasonMArchers Kommentar hinzu (es ist also klar, dass er feinkörniger verwendet werden kann als ein ganzes PowerShell-Skript)?
Peter Mortensen
183

Sie können auch den letzten Befehl aus dem Verlauf abrufen und EndExecutionTimevon seinem subtrahieren StartExecutionTime.

.\do_something.ps1  
$command = Get-History -Count 1  
$command.EndExecutionTime - $command.StartExecutionTime
Shay Levy
quelle
22
Versuchen Sie dies einmal: Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -descZeigen Sie Ihr PowerShell-Nutzungsmuster nach Tageszeit an. :-)
Keith Hill
18
+1, um damit herausfinden zu können, wie lange etwas gedauert hat, auch wenn Sie nicht damit gerechnet haben, dass es zu Beginn lange dauern würde, sodass Sie nicht daran gedacht haben, es in Measure-Command zu verpacken.
Chris Magnuson
3
Powershell ist manchmal großartig.
ConstantineK
Ich wünschte, ich könnte Ihnen mehr als nur +1 geben :)
David Ferenczy Rogožan
Ja, das ist großartig! Ich habe einen Einzeiler gemacht mit:$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
Phil
106

Verwenden Measure-Command

Beispiel

Measure-Command { <your command here> | Out-Host }

Mit der Pipe to Out-Hostkönnen Sie die Ausgabe des Befehls anzeigen, der ansonsten von verwendet wird Measure-Command.

Droj
quelle
Ich denke du meinst Measure-Command {<your command } | Out-Host - der Out-Host ist außerhalb des Skriptblocks
Peter McEvoy
1
@Peter - es muss sich innerhalb des Blocks befinden, andernfalls verbraucht Measure-Command die Ausgabe, bevor es zur Konsole geht.
Droj
1
Gotcha ... in diesem Fall brauchen Sie möglicherweise nicht einmal die Pfeife. Es sollte nur die Ergebnisse drucken, es sei denn, Sie haben es in einen anderen Block eingewickelt ...
Droj
2
Ist Out-Default vielleicht besser als Out-Host, weil es mit Skripten kompatibel ist? jsnover.com/blog/2013/12/07/write-host-considered-harmful
MarcH
1
Nun, ich habe Out-Default ausprobiert und es funktioniert auch in einem Terminal einwandfrei. Warum also nicht immer Out-Default verwenden? (Ich habe es nicht in einem Skript versucht, sorry)
MarcH
18

Einfache

function time($block) {
    $sw = [Diagnostics.Stopwatch]::StartNew()
    &$block
    $sw.Stop()
    $sw.Elapsed
}

kann dann als verwenden

time { .\some_command }

Möglicherweise möchten Sie die Ausgabe optimieren

Mike West
quelle
1
Measure-Commandverbirgt die Befehlsausgabe, daher ist diese Lösung manchmal besser.
Codekaizen
Dies ist eine fantastische Lösung, die die Ausgabe des Befehls berücksichtigt. Sie können es auch ohne geschweifte Klammern für einfache Befehle aufrufen, zum Beispiel: "time ls", genau wie in Unix.
Raúl Salinas-Monteagudo
5

Hier ist eine Funktion, die ich geschrieben habe und die ähnlich wie der Unix- timeBefehl funktioniert :

function time {
    Param(
        [Parameter(Mandatory=$true)]
        [string]$command,
        [switch]$quiet = $false
    )
    $start = Get-Date
    try {
        if ( -not $quiet ) {
            iex $command | Write-Host
        } else {
            iex $command > $null
        }
    } finally {
        $(Get-Date) - $start
    }
}

Quelle: https://gist.github.com/bender-the-greatest/741f696d965ed9728dc6287bdd336874

Bender der Größte
quelle
Die Frage lautete "Timing der Ausführung eines Befehls in PowerShell". Was hat das mit dem Timing eines Prozesses unter Unix zu tun?
Jean-Claude DeMars
5
Es ist eine Powershell-Funktion, die ich geschrieben habe und die zeigt, wie Sie die Ausführungszeit selbst berechnen Measure-Commandkönnen, anstatt eine oder eine der verschiedenen anderen Möglichkeiten zu verwenden, wie Sie die Ausführung in Powershell zeitlich festlegen können. Wenn Sie die ursprüngliche Frage gelesen haben, hat er nach etwas gefragt, das "wie der timeBefehl unter Linux" funktioniert .
Bender der Größte
3

Verwenden der Stoppuhr und Formatieren der verstrichenen Zeit:

Function FormatElapsedTime($ts) 
{
    $elapsedTime = ""

    if ( $ts.Minutes -gt 0 )
    {
        $elapsedTime = [string]::Format( "{0:00} min. {1:00}.{2:00} sec.", $ts.Minutes, $ts.Seconds, $ts.Milliseconds / 10 );
    }
    else
    {
        $elapsedTime = [string]::Format( "{0:00}.{1:00} sec.", $ts.Seconds, $ts.Milliseconds / 10 );
    }

    if ($ts.Hours -eq 0 -and $ts.Minutes -eq 0 -and $ts.Seconds -eq 0)
    {
        $elapsedTime = [string]::Format("{0:00} ms.", $ts.Milliseconds);
    }

    if ($ts.Milliseconds -eq 0)
    {
        $elapsedTime = [string]::Format("{0} ms", $ts.TotalMilliseconds);
    }

    return $elapsedTime
}

Function StepTimeBlock($step, $block) 
{
    Write-Host "`r`n*****"
    Write-Host $step
    Write-Host "`r`n*****"

    $sw = [Diagnostics.Stopwatch]::StartNew()
    &$block
    $sw.Stop()
    $time = $sw.Elapsed

    $formatTime = FormatElapsedTime $time
    Write-Host "`r`n`t=====> $step took $formatTime"
}

Verwendungsbeispiele

StepTimeBlock ("Publish {0} Reports" -f $Script:ArrayReportsList.Count)  { 
    $Script:ArrayReportsList | % { Publish-Report $WebServiceSSRSRDL $_ $CarpetaReports $CarpetaDataSources $Script:datasourceReport };
}

StepTimeBlock ("My Process")  {  .\do_something.ps1 }
Kiquenet
quelle
-2

Nur ein Wort zum Ziehen (falscher) Schlussfolgerungen aus einem der in den Antworten genannten Leistungsmessbefehle. Es gibt eine Reihe von Fallstricken, die berücksichtigt werden sollten, abgesehen von der bloßen Aufrufzeit einer (benutzerdefinierten) Funktion oder eines Befehls.

Sjoemelsoftware

'Sjoemelsoftware' zum niederländischen Wort des Jahres 2015 gewählt
Sjoemelen bedeutet Betrug, und das Wort sjoemelsoftware entstand aufgrund des Volkswagen-Abgasskandals. Die offizielle Definition lautet "Software zur Beeinflussung der Testergebnisse".

Persönlich denke ich, dass " Sjoemelsoftware " nicht immer absichtlich erstellt wurde, um Testergebnisse zu betrügen, sondern möglicherweise aus einer praktischen Situation resultiert, die den unten gezeigten Testfällen ähnelt.

Beispielsweise wird die Verwendung der aufgelisteten Leistungsmessbefehle, Language Integrated Query (LINQ) (1) , häufig als der schnellste Weg bezeichnet, um etwas zu erledigen, und dies ist häufig der Fall, aber sicherlich nicht immer! Jeder, der eine Geschwindigkeitssteigerung um den Faktor 40 oder mehr im Vergleich zu nativen PowerShell-Befehlen misst, misst wahrscheinlich falsch oder zieht eine falsche Schlussfolgerung.

Der Punkt ist, dass einige .Net-Klassen (wie LINQ) eine verzögerte Auswertung verwenden (auch als verzögerte Ausführung bezeichnet (2) ). Das heißt, wenn Sie einer Variablen einen Ausdruck zuweisen, scheint dies fast sofort erledigt zu sein, aber tatsächlich wurde noch nichts verarbeitet!

Lassen Sie vermuten , dass Sie dot-Quelle Ihren . .\Dosomething.ps1Befehl , die entweder eine Powershell oder ein anspruchsvolleres Linq Ausdruck hat (für die Einfachheit der Erklärung, habe ich direkt die Ausdrücke direkt in die eingebettet Measure-Command):

$Data = @(1..100000).ForEach{[PSCustomObject]@{Index=$_;Property=(Get-Random)}}

(Measure-Command {
    $PowerShell = $Data.Where{$_.Index -eq 12345}
}).totalmilliseconds
864.5237

(Measure-Command {
    $Linq = [Linq.Enumerable]::Where($Data, [Func[object,bool]] { param($Item); Return $Item.Index -eq 12345})
}).totalmilliseconds
24.5949

Das Ergebnis scheint offensichtlich zu sein. Der spätere Linq- Befehl ist etwa 40-mal schneller als der erste PowerShell- Befehl. Leider ist es nicht so einfach ...

Lassen Sie uns die Ergebnisse anzeigen:

PS C:\> $PowerShell

Index  Property
-----  --------
12345 104123841

PS C:\> $Linq

Index  Property
-----  --------
12345 104123841

Wie erwartet sind die Ergebnisse dieselben, aber wenn Sie genau aufgepasst haben, werden Sie feststellen, dass die Anzeige der $LinqErgebnisse viel länger gedauert hat als die $PowerShellErgebnisse.
Lassen Sie uns dies speziell messen , indem wir nur eine Eigenschaft des resultierenden Objekts abrufen:

PS C:\> (Measure-Command {$PowerShell.Property}).totalmilliseconds
14.8798
PS C:\> (Measure-Command {$Linq.Property}).totalmilliseconds
1360.9435

Das Abrufen einer Eigenschaft des Objekts dauerte etwa einen Faktor 90 länger als das Abrufen $Linqdes $PowerShellObjekts, und das war nur ein einzelnes Objekt!

Beachten Sie auch eine andere Gefahr, dass bestimmte Schritte bei einem erneuten Ausführen möglicherweise viel schneller als zuvor angezeigt werden. Dies liegt daran, dass einige der Ausdrücke zwischengespeichert wurden.

Fazit: Wenn Sie die Leistung zwischen zwei Funktionen vergleichen möchten, müssen Sie diese in Ihrem verwendeten Fall implementieren, mit einer neuen PowerShell-Sitzung beginnen und Ihre Schlussfolgerung auf die tatsächliche Leistung der gesamten Lösung stützen.

(1) Für weitere Hintergrundinformationen und Beispiele zu PowerShell und LINQ empfehle ich diese Website: Hochleistungs-PowerShell mit LINQ
(2) Ich denke, es gibt einen geringfügigen Unterschied zwischen den beiden Konzepten, da bei einer verzögerten Bewertung das Ergebnis bei Bedarf berechnet wird Aufgeschobene Ausführung , wenn das Ergebnis berechnet wird, wenn das System inaktiv ist

Eisen
quelle
Die Absicht dieser Antwort ist es, Menschen auf eine häufige Frage für ein allgemeines Missverständnis in Bezug auf Timing-Befehle in PowerShell verweisen zu können , wie ich es gerade für eine sich wiederholende Frage getan habe, wie: Powershell-Frage - Suche nach der schnellsten Methode zum Durchlaufen von 500.000 Objekten Suche nach einer Übereinstimmung in einem anderen 500k-Objektarray
iRon