Cronometrando a execução de um comando no PowerShell


217

Existe uma maneira simples de cronometrar a execução de um comando no PowerShell, como o comando 'time' no Linux?
Eu vim com isso:

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

Mas eu gostaria de algo mais simples como

time .\do_something.ps1

Respostas:


337

Sim.

Measure-Command { .\do_something.ps1 }

Observe que uma desvantagem menor Measure-Commandé que você não vê stdoutsaída.

[Atualização, graças a @JasonMArcher] Você pode corrigir isso canalizando a saída do comando para algum comando que grava no host, por exemplo, Out-Defaultpara que se torne:

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

Outra maneira de ver a saída seria usar a Stopwatchclasse .NET assim:

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

114
Você também pode ver resultados como este, Measure-Command {ps | Fora do padrão}. Ou qualquer outra coisa que grave diretamente no host, que pode ou não ser útil.
JasonMArcher

18
Eu peguei essa solução e escrevi uma função que pode ser útil para outra pessoa. gist.github.com/2206444 - Exemplo: time { ping -n 1 google.com } -Samples 10executará os 10tempos de comando e retornará o tempo médio, mínimo e máximo necessário. Você pode adicionar -Silentpara engolir STDOUT.
joshuapoehls

13
Minha preferência seria atribuir o resultado do Measure-Command a uma variável, como $t = Measure-Command {<<your command or code block>>}. Experimentá-lo e, em seguida, digite $tna linha de comando para ver os resultados e todas as propriedades que têm acesso, como $t.Milliseconds, $t.TotalSeconds, etc. Então, podemos escrever a qualquer saída que queremos, por exemplo,Write-Host That command took $t.TotalSeconds to complete.
Baodad

o que é mais rápido de usar? net.stopwatch ou medida de comando, ou apenas comparando dois get-data vars ... (quero dizer, o que é mais eficiente para manter permanentemente em um script?)
Hicsy

Talvez inclua a essência do comentário de JasonMArcher (para que fique claro que ele pode ser usado de maneira mais refinada que um script inteiro do PowerShell)?
Peter Mortensen

183

Você também pode obter o último comando do histórico e subtraí-lo EndExecutionTimedo seu StartExecutionTime.

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

22
Tente isso em algum momento: Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -descpara ver seu padrão de uso do PowerShell por hora do dia. :-)
Keith Hill

18
+1 por poder usar isso para descobrir quanto tempo levou algo, mesmo quando você não esperava que demorasse muito tempo quando começou, para não pensar em incluí-lo no Measure-Command.
22620 Chris Magnuson

3
PowerShell às vezes é incrível.
ConstantineK

Eu gostaria de poder dar a você mais do que apenas +1 :) #
1013 David Ferenczy Rogožan

Sim, isso é ótimo! Eu fiz um one-liner usando:$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
Phil

106

Usar Measure-Command

Exemplo

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

O canal para Out-Hostpermite ver a saída do comando, que é consumido de outra forma Measure-Command.


Eu acho que você quer dizer Measure-Command {<your command } | Out-Host - o Out-Host está fora do bloco de script
Peter McEvoy

1
@ Peter - ele precisa estar dentro do bloco, caso contrário o Measure-Command consome a saída antes de ir para o console.
Droj

1
Entendi ... nesse caso, você pode nem precisar do cachimbo. Ele deve apenas imprimir os resultados, a menos que você tê-lo envolvido em algum outro bloco ....
Droj

2
Out-Default talvez seja melhor que Out-Host porque é compatível com scripts? jsnover.com/blog/2013/12/07/write-host-considered-harmful
Março

1
Bem, eu tentei Out-Default e ele funciona muito bem em um terminal também, então por que não usar Out-Default sempre? (Eu não tentei isso em um script sorry)
Março

18

Simples

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

então pode usar como

time { .\some_command }

Você pode querer ajustar a saída


1
Measure-Commandoculta a saída do comando, então essa solução às vezes é melhor.
Codigo

Esta é uma solução fantástica, que respeita a saída do comando. Você também pode invocá-lo sem chaves para comandos simples, por exemplo: "time ls", exatamente como faria no Unix.
Raúl Salinas-Monteagudo

5

Aqui está uma função que escrevi que funciona de maneira semelhante ao 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
    }
}

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


A pergunta era "Cronometrando a execução de um comando no PowerShell". O que isso tem a ver com o tempo de um processo usando o Unix?
Jean-Claude DeMars

5
É uma função do Powershell que escrevi, que mostra como calcular o tempo de execução em vez de usar Measure-Commandou uma das várias outras maneiras pelas quais você pode executar o tempo no Powershell. Se você leu a pergunta original, ele pediu algo que funcionasse "como o timecomando no Linux".
Bender the Great

3

Usando o cronômetro e formatação do tempo decorrido:

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

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

-2

Apenas uma palavra sobre como tirar conclusões (incorretas) de qualquer um dos comandos de medição de desempenho mencionados nas respostas. Há várias armadilhas que devem ser levadas em consideração, além de considerar o tempo de invocação simples de uma função ou comando (personalizado).

Sjoemelsoftware

'Sjoemelsoftware' votou a palavra holandesa do ano 2015
Sjoemelen significa trapaça, e a palavra sjoemelsoftware surgiu devido ao escândalo de emissões da Volkswagen. A definição oficial é "software usado para influenciar os resultados dos testes".

Pessoalmente, acho que o " Sjoemelsoftware " nem sempre é criado deliberadamente para enganar os resultados dos testes, mas pode ter origem em acomodar situações práticas semelhantes aos casos de teste, como mostrado abaixo.

Como exemplo, usando os comandos de medição de desempenho listados, Language Integrated Query (LINQ) (1) , é frequentemente qualificado como a maneira mais rápida de fazer alguma coisa e geralmente é, mas certamente nem sempre! Quem mede um aumento de velocidade de um fator 40 ou mais em comparação com os comandos nativos do PowerShell, provavelmente está medindo incorretamente ou tirando uma conclusão incorreta.

O ponto é que algumas classes .Net (como LINQ) usando uma avaliação lenta (também chamada de execução adiada) (2) ). Isso significa que, quando atribui uma expressão a uma variável, ela quase imediatamente parece ter sido executada, mas na verdade ainda não processou nada!

Vamos supor que você pontue sua fonte. .\Dosomething.ps1 comando que possui uma expressão do PowerShell ou uma Linq mais sofisticada (para facilitar a explicação, integrei diretamente as expressões diretamente no 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

O resultado parece óbvio, o comando Linq posterior é cerca de 40 vezes mais rápido que o primeiro PowerShell comando do . Infelizmente, não é tão simples assim ...

Vamos exibir os resultados:

PS C:\> $PowerShell

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

PS C:\> $Linq

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

Como esperado, os resultados são os mesmos, mas se você prestar muita atenção, perceberá que demorou muito mais tempo para exibir os $Linqresultados do que os $PowerShellresultados.
Vamos medir especificamente isso apenas recuperando uma propriedade do objeto resultante:

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

Demorou cerca de um fator 90 a mais para recuperar uma propriedade do $Linqobjeto que o $PowerShellobjeto e esse era apenas um único objeto!

Observe também uma outra armadilha: se você fizer isso de novo, algumas etapas podem parecer muito mais rápidas do que antes, isso ocorre porque algumas das expressões foram armazenadas em cache.

Resumindo, se você deseja comparar o desempenho entre duas funções, será necessário implementá-las no seu caso usado, comece com uma nova sessão do PowerShell e baseie sua conclusão no desempenho real da solução completa.

(1) Para obter mais informações e exemplos sobre o PowerShell e o LINQ, recomendo este site: PowerShell de alto desempenho com o LINQ
(2) Acho que há uma pequena diferença entre os dois conceitos, pois na avaliação lenta, o resultado é calculado quando necessário , ao contrário de execução adiada onde o resultado é calculado quando o sistema está ocioso


A intenção desta resposta é ser capaz de encaminhar as pessoas a uma pergunta comum para um equívoco geral no que diz respeito aos comandos de temporização no PowerShell, como acabei de fazer para uma pergunta repetida como: Pergunta do PowerShell - Procurando o método mais rápido para percorrer 500 mil objetos procurando uma correspondência em outra matriz de objetos de 500k
iRon 27/01
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.