ChatGPT解决这个技术问题 Extra ChatGPT

Timing a command's execution in PowerShell

Is there a simple way to time the execution of a command in PowerShell, like the 'time' command in Linux? I came up with this:

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

But I would like something simpler like

time .\do_something.ps1

O
Oliver

Yup.

Measure-Command { .\do_something.ps1 }

Note that one minor downside of Measure-Command is that you see no stdout output.

[Update, thanks to @JasonMArcher] You can fix that by piping the command output to some commandlet that writes to the host, e.g. Out-Default so it becomes:

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

Another way to see the output would be to use the .NET Stopwatch class like this:

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

You can also see output like this, Measure-Command {ps | Out-Default}. Or anything else that writes directly to the host, which may or may not be useful.
I took this solution and wrote a function that may be useful to someone else. gist.github.com/2206444 -- Example: time { ping -n 1 google.com } -Samples 10 will run the command 10 times and return the average, minimum and maximum time taken. You can add -Silent to swallow STDOUT.
My preference would be to assign the result of Measure-Command to a variable, like $t = Measure-Command {<<your command or code block>>} . Try it out and then type $t at the prompt to see your results and all the properties you have access to, like $t.Milliseconds, $t.TotalSeconds, etc. Then we can write to whatever output we want, for instance, Write-Host That command took $t.TotalSeconds to complete.
what's faster to use? net.stopwatch, or measure-command, or just comparing two get-date vars... (I mean what's more efficient to keep permanently in a script?)
Similar to | Out-Default, you can also use -OutVariable to store the output object, e.g. $m = Measure-Command { Get-WinEvent -LogName System -MaxEvents 1 -OutVariable events }; $events.Message; $m.TotalSeconds
b
bdukes

You can also get the last command from history and subtract its EndExecutionTime from its StartExecutionTime.

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

Try this sometime: Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -desc to see your PowerShell usage pattern by hour of day. :-)
+1 for being able to use this to find out how long something took even when you didn't expect it to take a long time when you started so you didn't think to wrap it in Measure-Command.
powershell is awesome sometimes.
Yes, this is great! I made a one-liner using: $command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
Important note, I don't know how true this was in '10, but as of some point before this comment the command object has a "duration" field, so manually calculating from the two FooExecutionTime fields is no longer required.
t
thegreendroid

Use Measure-Command

Example

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

The pipe to Out-Host allows you to see the output of the command, which is otherwise consumed by Measure-Command.


I think you mean Measure-Command {<your command } | Out-Host - the Out-Host is outside the script block
@Peter - it needs to be inside the block, otherwise Measure-Command consumes the output before it goes to the console.
Gotcha... in that case, you might not even need the pipe. It should just print the results, unless you have it wrapped in some other block....
Is Out-Default maybe better than Out-Host because compatible with scripting? jsnover.com/blog/2013/12/07/write-host-considered-harmful
Well I tried Out-Default and it works just fine in a terminal too, so why not use Out-Default always? (I haven't tried it in a script sorry)
M
Mike West

Simples

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

then can use as

time { .\some_command }

You may want to tweak the output


Measure-Command hides command output, so this solution is sometimes better.
This is a fantastic solution, which respects the command's output. You can also invoke it without curly braces for simple commands, for example: "time ls", exactly as you would in Unix.
I'm using this to measure function calls. However you better add the arguments as well to the calling convention, like so: & $block @Args.
B
Bender

Here's a function I wrote which works similarly to the Unix time command:

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

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


The question was "Timing a command's execution in PowerShell". What does that have to do with timing a process using Unix?
It's a Powershell function I wrote, which shows how to calculate the execution time yourself as opposed to using Measure-Command or one of the various other ways you can time execution in Powershell. If you read the original question, he asked for something that works "like the time command in Linux".
K
Kiquenet

Using Stopwatch and formatting elapsed time:

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

Usage Samples

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

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

J
John Freeman

All the answers so far fall short of the questioner's (and my) desire to time a command by simply adding "time " to the start of the command line. Instead, they all require wrapping the command in brackets ({}) to make a block. Here is a short function that works more like time on Unix:

Function time() {
  $command = $args -join ' '
  Measure-Command { Invoke-Expression $command | Out-Default }
}

M
MaxCreatesCode

A more PowerShell inspired way to access the value of properties you care about:

$myCommand = .\do_something.ps1
Measure-Command { Invoke-Expression $myCommand } | Select -ExpandProperty Milliseconds
    4

As Measure-Command returns a TimeSpan object.

note: The TimeSpan object also has TotalMilliseconds as a double (such as 4.7322 TotalMilliseconds in my case above) which might be useful to you. Just like TotalSeconds, TotalDays, etc.


J
JamesNEW

(measure-commmand{your command}).totalseconds

for instance

(measure-commmand{.\do_something.ps1}).totalseconds


i
iRon

Just a word on drawing (incorrect) conclusions from any of the performance measurement commands referred to in the answers. There are a number of pitfalls that should taken in consideration aside from looking to the bare invocation time of a (custom) function or command.

Sjoemelsoftware

'Sjoemelsoftware' voted Dutch word of the year 2015 Sjoemelen means cheating, and the word sjoemelsoftware came into being due to the Volkswagen emissions scandal. The official definition is "software used to influence test results".

Personally, I think that "Sjoemelsoftware" is not always deliberately created to cheat test results but might originate from accommodating practical situation that are similar to test cases as shown below.

As an example, using the listed performance measurement commands, Language Integrated Query (LINQ)(1), is often qualified as the fasted way to get something done and it often is, but certainly not always! Anybody who measures a speed increase of a factor 40 or more in comparison with native PowerShell commands, is probably incorrectly measuring or drawing an incorrect conclusion.

The point is that some .Net classes (like LINQ) using a lazy evaluation (also referred to as deferred execution(2)). Meaning that when assign an expression to a variable, it almost immediately appears to be done but in fact it didn't process anything yet!

Let presume that you dot-source your . .\Dosomething.ps1 command which has either a PowerShell or a more sophisticated Linq expression (for the ease of explanation, I have directly embedded the expressions directly into the 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

The result appears obvious, the later Linq command is a about 40 times faster than the first PowerShell command. Unfortunately, it is not that simple...

Let's display the results:

PS C:\> $PowerShell

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

PS C:\> $Linq

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

As expected, the results are the same but if you have paid close attention, you will have noticed that it took a lot longer to display the $Linq results then the $PowerShell results.
Let's specifically measure that by just retrieving a property of the resulted object:

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

It took about a factor 90 longer to retrieve a property of the $Linq object then the $PowerShell object and that was just a single object!

Also notice an other pitfall that if you do it again, certain steps might appear a lot faster then before, this is because some of the expressions have been cached.

Bottom line, if you want to compare the performance between two functions, you will need to implement them in your used case, start with a fresh PowerShell session and base your conclusion on the actual performance of the complete solution.

(1) For more background and examples on PowerShell and LINQ, I recommend tihis site: High Performance PowerShell with LINQ
(2) I think there is a minor difference between the two concepts as with lazy evaluation the result is calculated when needed as apposed to deferred execution were the result is calculated when the system is idle


The intention of this answer is to be able to refer people to a common question for a general misconception with regards to timing commands in PowerShell, as I just did for a repeating question like: Powershell question - Looking for fastest method to loop through 500k objects looking for a match in another 500k object array