Tiempo de ejecución de un comando en PowerShell

217

¿Hay una manera simple de cronometrar la ejecución de un comando en PowerShell, como el comando 'time' en Linux?
Se me ocurrió esto:

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

Pero me gustaría algo más simple como

time .\do_something.ps1
Paolo Tedesco
fuente

Respuestas:

337

Sip.

Measure-Command { .\do_something.ps1 }

Tenga en cuenta que una desventaja menor Measure-Commandes que no ve stdoutsalida.

[Actualización, gracias a @JasonMArcher] Puede solucionarlo canalizando la salida del comando a algún comando que escriba en el host, por ejemplo, Out-Defaultse convierte en:

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

Otra forma de ver el resultado sería usar la Stopwatchclase .NET de esta manera:

$sw = [Diagnostics.Stopwatch]::StartNew()
.\do_something.ps1
$sw.Stop()
$sw.Elapsed
Keith Hill
fuente
114
También puede ver resultados como este, Measure-Command {ps | Out-Default}. O cualquier otra cosa que escriba directamente en el host, que puede ser útil o no.
JasonMArcher
18
Tomé esta solución y escribí una función que puede ser útil para otra persona. gist.github.com/2206444 - Ejemplo: time { ping -n 1 google.com } -Samples 10ejecutará los 10tiempos de comando y devolverá el tiempo promedio, mínimo y máximo empleado. Puede agregar -Silentpara tragar STDOUT.
Joshuapoehls
13
Mi preferencia sería asignar el resultado de Measure-Command a una variable, como $t = Measure-Command {<<your command or code block>>}. Probarlo y luego escriba $ten el indicador para ver los resultados y todas las propiedades que tiene acceso, como $t.Milliseconds, $t.TotalSeconds, etc. Entonces podemos escribir a cualquier salida que queremos, por ejemplo,Write-Host That command took $t.TotalSeconds to complete.
Baodad
¿Qué es más rápido de usar? net.stopwatch, o measure-command, o simplemente comparando dos variables get-date ... (Quiero decir, ¿qué es más eficiente para mantener permanentemente en un script?)
Hicsy
¿Quizás incluir la esencia del comentario de JasonMArcher (por lo que está claro que se puede usar de una manera más precisa que un script completo de PowerShell)?
Peter Mortensen el
183

También puede obtener el último comando del historial y restarlo EndExecutionTimede él StartExecutionTime.

.\do_something.ps1  
$command = Get-History -Count 1  
$command.EndExecutionTime - $command.StartExecutionTime
Shay Levy
fuente
22
Pruebe esto en algún momento: Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -descpara ver su patrón de uso de PowerShell por hora del día. :-)
Keith Hill
18
+1 por poder usar esto para descubrir cuánto tiempo tomó algo, incluso cuando no esperaba que tomara mucho tiempo cuando comenzó, por lo que no pensó en envolverlo en Measure-Command.
Chris Magnuson
3
Powershell es impresionante a veces.
ConstantineK
Desearía poder darte más que solo +1 :)
David Ferenczy Rogožan
Sí, esto es genial! Hice una frase usando:$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
Phil
106

Utilizar Measure-Command

Ejemplo

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

La canalización a le Out-Hostpermite ver la salida del comando, que de otro modo es consumido por Measure-Command.

Droj
fuente
Creo que te refieres Measure-Command {<your command } | Out-Host - el Out-Host está fuera del bloque de script
Peter McEvoy
1
@Peter: debe estar dentro del bloque, de lo contrario, Measure-Command consume la salida antes de que vaya a la consola.
Droj
1
Te tengo ... en ese caso, tal vez ni siquiera necesites la tubería. Solo debe imprimir los resultados, a menos que lo tenga envuelto en algún otro bloque ...
Droj
2
¿Out-Default puede ser mejor que Out-Host porque es compatible con las secuencias de comandos? jsnover.com/blog/2013/12/07/write-host-considered-harmful
MarcH
1
Bueno, probé Out-Default y también funciona bien en un terminal, así que ¿por qué no usar Out-Default siempre? (No lo he probado en un guión lo siento)
MarcH
18

Simples

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

entonces puede usar como

time { .\some_command }

Es posible que desee ajustar la salida

Mike West
fuente
1
Measure-Commandoculta la salida del comando, por lo que esta solución a veces es mejor.
codekaizen
Esta es una solución fantástica, que respeta la salida del comando. También puede invocarlo sin llaves para comandos simples, por ejemplo: "time ls", exactamente como lo haría en Unix.
Raúl Salinas-Monteagudo
5

Aquí hay una función que escribí que funciona de manera similar al timecomando Unix :

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

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

Bender el más grande
fuente
La pregunta era "Tiempo de ejecución de un comando en PowerShell". ¿Qué tiene que ver eso con cronometrar un proceso usando Unix?
Jean-Claude DeMars
55
Es una función de Powershell que escribí, que muestra cómo calcular el tiempo de ejecución usted mismo en lugar de usar Measure-Commando una de las otras formas en que puede cronometrar la ejecución en Powershell. Si lees la pregunta original, él pidió algo que funcione "como el timecomando en Linux".
Bender the Greatest
3

Usando el cronómetro y formateando el tiempo transcurrido:

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

Muestras de uso

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

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

Solo una palabra sobre cómo sacar conclusiones (incorrectas) de cualquiera de los comandos de medición de rendimiento mencionados en las respuestas. Hay una serie de dificultades que deben tenerse en cuenta además de mirar el tiempo de invocación de una función o comando (personalizado).

Sjoemelsoftware

'Sjoemelsoftware' votó la palabra holandesa del año 2015
Sjoemelen significa trampa, y la palabra sjoemelsoftware surgió debido al escándalo de emisiones de Volkswagen. La definición oficial es "software utilizado para influir en los resultados de las pruebas".

Personalmente, creo que " Sjoemelsoftware " no siempre se crea deliberadamente para engañar a los resultados de las pruebas, pero podría originarse en una situación práctica que sea similar a los casos de prueba como se muestra a continuación.

Como ejemplo, el uso de los comandos de medición de rendimiento enumerados, Language Integrated Query (LINQ) (1) , a menudo se califica como la forma más rápida de hacer algo y, a menudo, ¡pero ciertamente no siempre! Cualquiera que mida un aumento de velocidad de un factor 40 o más en comparación con los comandos nativos de PowerShell, probablemente esté midiendo o sacando una conclusión incorrecta.

El punto es que algunas clases .Net (como LINQ) usan una evaluación diferida (también conocida como ejecución diferida (2) ). Lo que significa que cuando se asigna una expresión a una variable, parece que se hace casi de inmediato, ¡pero de hecho todavía no procesó nada!

Supongamos que su fuente de puntos. .\Dosomething.ps1 tiene su comando que tiene una expresión de PowerShell o una expresión de Linq más sofisticada (para facilitar la explicación, he incrustado directamente las expresiones directamente en 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

El resultado parece obvio, el último comando de Linq es aproximadamente 40 veces más rápido que el primer comando de PowerShell . Desafortunadamente, no es tan simple ...

Vamos a mostrar los resultados:

PS C:\> $PowerShell

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

PS C:\> $Linq

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

Como era de esperar, los resultados son los mismos, pero si ha prestado mucha atención, habrá notado que tomó mucho más tiempo mostrar los $Linqresultados que los $PowerShellresultados.
Midamos específicamente eso solo recuperando una propiedad del objeto resultante:

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

¡Le tomó alrededor de un factor 90 más tiempo recuperar una propiedad del $Linqobjeto que el $PowerShellobjeto y eso fue solo un objeto!

Observe también otro error: si lo vuelve a hacer, ciertos pasos pueden aparecer mucho más rápido que antes, esto se debe a que algunas de las expresiones se han almacenado en caché.

En pocas palabras, si desea comparar el rendimiento entre dos funciones, deberá implementarlas en su caso usado, comenzar con una nueva sesión de PowerShell y basar su conclusión en el rendimiento real de la solución completa.

(1) Para obtener más antecedentes y ejemplos sobre PowerShell y LINQ, le recomiendo este sitio: PowerShell de alto rendimiento con LINQ
(2) Creo que hay una pequeña diferencia entre los dos conceptos, ya que con la evaluación diferida el resultado se calcula cuando es necesario según lo previsto ejecución diferida donde el resultado se calcula cuando el sistema está inactivo

hierro
fuente
La intención de esta respuesta es poder referir a las personas a una pregunta común para una idea errónea general con respecto a los comandos de temporización en PowerShell, como acabo de hacer para una pregunta repetitiva como: Pregunta Powershell: buscando el método más rápido para recorrer 500k objetos buscando una coincidencia en otra matriz de objetos de 500k
iRon