Temporizzazione dell'esecuzione di un comando in PowerShell


217

Esiste un modo semplice per temporizzare l'esecuzione di un comando in PowerShell, come il comando 'time' in Linux?
Ho pensato a questo:

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

Ma vorrei qualcosa di più semplice

time .\do_something.ps1

Risposte:


337

Sì.

Measure-Command { .\do_something.ps1 }

Si noti che un aspetto negativo minore di Measure-Commandè che non viene visualizzato alcun stdoutoutput.

[Aggiorna, grazie a @JasonMArcher] Puoi risolverlo eseguendo il piping dell'output del comando su alcuni commandlet che scrivono sull'host, ad esempio Out-Defaultcosì diventa:

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

Un altro modo di vedere l'output sarebbe usare la Stopwatchclasse .NET in questo modo:

$sw = [Diagnostics.Stopwatch]::StartNew()
.\do_something.ps1
$sw.Stop()
$sw.Elapsed

114
Puoi anche vedere un output come questo, Measure-Command {ps | Out-default}. O qualsiasi altra cosa che scriva direttamente sull'host, che può essere utile o meno.
JasonMArcher,

18
Ho preso questa soluzione e ho scritto una funzione che potrebbe essere utile a qualcun altro. gist.github.com/2206444 - Esempio: time { ping -n 1 google.com } -Samples 10eseguirà i 10tempi di comando e restituirà il tempo medio, minimo e massimo impiegato. Puoi aggiungere -Silentper ingoiare STDOUT.
joshuapoehls,

13
La mia preferenza sarebbe quella di assegnare il risultato di Measure-Command a una variabile, come $t = Measure-Command {<<your command or code block>>}. Provalo e quindi digitare $tal prompt per visualizzare i risultati e tutte le proprietà si ha accesso, come $t.Milliseconds, $t.TotalSecondsper altro, allora possiamo scrivere su qualunque uscita che vogliamo, per esempio,Write-Host That command took $t.TotalSeconds to complete.
Baodad

cosa c'è di più veloce da usare? net.stopwatch, o measure-command, o semplicemente confrontando due variabili di get-date ... (Voglio dire, cosa c'è di più efficiente da tenere permanentemente in uno script?)
Hicsy

Forse includi l'essenza del commento di JasonMArcher (quindi è chiaro che può essere usato in un modo più preciso di un intero script di PowerShell)?
Peter Mortensen,

183

Puoi anche ottenere l'ultimo comando dalla cronologia e sottrarlo EndExecutionTimedal suo StartExecutionTime.

.\do_something.ps1  
$command = Get-History -Count 1  
$command.EndExecutionTime - $command.StartExecutionTime

22
Provalo qualche volta: Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -descper vedere il tuo modello di utilizzo di PowerShell per ora del giorno. :-)
Keith Hill il

18
+1 per poterlo usare per scoprire quanto tempo impiegava qualcosa anche quando non ti aspettavi che impiegasse molto tempo quando hai iniziato, quindi non hai pensato di avvolgerlo in Measure-Command.
Chris Magnuson,

3
Powershell è fantastico a volte.
ConstantineK,

Vorrei
poterti

Sì, è fantastico! Ho fatto un one-liner usando:$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
Phil

106

Uso Measure-Command

Esempio

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

La pipe per Out-Hostti permette di vedere l'output del comando, che viene altrimenti consumato da Measure-Command.


Penso che intendi Measure-Command {<your command } | Out-Host - l'Out-Host è fuori dal blocco della sceneggiatura
Peter McEvoy il

1
@Peter - deve trovarsi all'interno del blocco, altrimenti Measure-Command consuma l'output prima di andare alla console.
Droj,

1
Gotcha ... in quel caso, potresti non aver nemmeno bisogno della pipa. Dovrebbe solo stampare i risultati, a meno che tu non l'abbia racchiuso in qualche altro blocco ....
Droj

2
Out-Default è forse migliore di Out-Host perché compatibile con gli script? jsnover.com/blog/2013/12/07/write-host-considered-harmful
MarcH

1
Beh, ho provato Out-Default e funziona bene anche in un terminale, quindi perché non usare sempre Out-Default? (Non l'ho provato in uno script, mi dispiace)
MarcH,

18

semplici

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

quindi può usare come

time { .\some_command }

Potresti voler modificare l'output


1
Measure-Commandnasconde l'output del comando, quindi questa soluzione a volte è migliore.
codekaizen,

Questa è una soluzione fantastica, che rispetta l'output del comando. Puoi anche invocarlo senza parentesi graffe per comandi semplici, ad esempio: "time ls", esattamente come faresti con Unix.
Raúl Salinas-Monteagudo,

5

Ecco una funzione che ho scritto che funziona in modo simile a Unix time comando :

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
    }
}

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


La domanda era "Temporizzazione dell'esecuzione di un comando in PowerShell". Cosa c'entra questo con il cronometraggio di un processo usando Unix?
Jean-Claude DeMars,

5
È una funzione di Powershell che ho scritto, che mostra come calcolare tu stesso il tempo di esecuzione invece di utilizzare Measure-Commando uno dei vari altri modi in cui puoi eseguire il tempo di esecuzione in Powershell. Se leggi la domanda originale, ha chiesto qualcosa che funzioni "come il timecomando in Linux".
Bender the Greatest,

3

Uso del cronometro e formattazione del tempo trascorso:

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"
}

Esempi di utilizzo

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

StepTimeBlock ("My Process")  {  .\do_something.ps1 }

-2

Solo una parola per trarre conclusioni (errate) da uno qualsiasi dei comandi di misurazione delle prestazioni a cui si fa riferimento nelle risposte. Ci sono una serie di insidie ​​che dovrebbero essere prese in considerazione oltre a guardare al tempo di invocazione nudo di una funzione o comando (personalizzato).

Sjoemelsoftware

'Sjoemelsoftware' ha votato la parola olandese dell'anno 2015
Sjoemelen significa barare, e la parola sjoemelsoftware è nata a causa dello scandalo sulle emissioni di Volkswagen. La definizione ufficiale è "software utilizzato per influenzare i risultati dei test".

Personalmente, penso che " Sjoemelsoftware " non sia sempre stato creato deliberatamente per ingannare i risultati dei test, ma potrebbe derivare da una situazione pratica accomodante simile ai casi di test come mostrato di seguito.

Ad esempio, l'utilizzo dei comandi di misurazione delle prestazioni elencati, Language Integrated Query (LINQ) (1) , è spesso qualificato come il modo più veloce per fare qualcosa e spesso lo è, ma certamente non sempre! Chiunque misura un aumento di velocità di un fattore 40 o superiore rispetto ai comandi nativi di PowerShell, probabilmente sta misurando erroneamente o sta tracciando una conclusione errata.

Il punto è che alcune classi .Net (come LINQ) utilizzano una valutazione lazy (anche definita esecuzione differita (2) ). Ciò significa che quando si assegna un'espressione a una variabile, sembra quasi immediatamente fatto, ma in realtà non ha ancora elaborato nulla!

Supponiamo che tu dot-source il tuo . .\Dosomething.ps1comando che ha una PowerShell o un'espressione Linq più sofisticata (per semplicità di spiegazione, ho incorporato direttamente le espressioni direttamente in 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

Il risultato sembra ovvio, il successivo comando Linq è circa 40 volte più veloce del primo comando PowerShell . Purtroppo, è non è così semplice ...

Mostriamo i risultati:

PS C:\> $PowerShell

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

PS C:\> $Linq

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

Come previsto, i risultati sono gli stessi ma se hai prestato molta attenzione, avrai notato che ci è voluto molto più tempo per visualizzare i $Linqrisultati, quindi i $PowerShellrisultati.
Misuriamo in modo specifico questo semplicemente recuperando una proprietà dell'oggetto risultante:

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

Ci è voluto circa un fattore 90 in più per recuperare una proprietà $Linqdell'oggetto quindi l' $PowerShelloggetto e quello era solo un singolo oggetto!

Notare anche un'altra insidie ​​che se lo fai di nuovo, alcuni passaggi potrebbero apparire molto più velocemente rispetto a prima, questo perché alcune delle espressioni sono state memorizzate nella cache.

In conclusione, se si desidera confrontare le prestazioni tra due funzioni, sarà necessario implementarle nel caso usato, iniziare con una nuova sessione di PowerShell e basare le proprie conclusioni sulle prestazioni effettive della soluzione completa.

(1) Per ulteriori informazioni ed esempi su PowerShell e LINQ, ti consiglio questo sito: PowerShell ad alte prestazioni con LINQ
(2) Penso che ci sia una differenza minore tra i due concetti poiché con una valutazione lenta il risultato viene calcolato quando necessario secondo esecuzione differita se il risultato è calcolato quando il sistema è inattivo


L'intenzione di questa risposta è quella di essere in grado di indirizzare le persone a una domanda comune per un malinteso generale riguardo ai comandi di temporizzazione in PowerShell, come ho appena fatto per una domanda ripetuta come: Domanda Powershell - Alla ricerca del metodo più veloce per eseguire il ciclo di oggetti 500k alla ricerca di una partita in un altro array di oggetti da
500k
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.