319

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
iRon
  • 20,463
  • 10
  • 53
  • 79
Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193
  • 1
    Does this answer your question? [How do I measure execution time of a command on the Windows command line?](https://stackoverflow.com/questions/673523/how-do-i-measure-execution-time-of-a-command-on-the-windows-command-line) – Josh Correia Jul 28 '22 at 19:22

10 Answers10

505

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
Oliver
  • 9,239
  • 9
  • 69
  • 100
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 134
    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. – JasonMArcher Aug 21 '10 at 05:23
  • 24
    I took this solution and wrote a function that may be useful to someone else. https://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. – joshuapoehls Mar 26 '12 at 18:11
  • 15
    My preference would be to assign the result of Measure-Command to a variable, like `$t = Measure-Command {<>}` . 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.` – Baodad Jan 21 '14 at 19:49
  • 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?) – Hicsy Sep 20 '17 at 04:00
  • Perhaps include the gist of JasonMArcher's comment (so it is clear it can be used in a more fine-grained way than a whole PowerShell script)? – Peter Mortensen Nov 28 '18 at 17:27
  • 3
    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` – JPvRiel Sep 17 '19 at 12:19
  • Just for clarity, this measures elapsed time (wall time), not cpu time (execution time) – john v kumpf Jan 07 '20 at 22:31
226

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
bdukes
  • 152,002
  • 23
  • 148
  • 175
Shay Levy
  • 121,444
  • 32
  • 184
  • 206
  • 26
    Try this sometime: `Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -desc` to see your PowerShell usage pattern by hour of day. :-) – Keith Hill Aug 18 '10 at 21:41
  • 27
    +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. – Chris Magnuson Feb 01 '15 at 17:34
  • I wish I can give you more than just +1 :) – David Ferenczy Rogožan Nov 29 '18 at 21:37
  • 5
    Yes, this is great! I made a one-liner using: ``$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)`` – Phil Mar 27 '19 at 16:56
  • 4
    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. – A. Wilson Jul 31 '20 at 00:27
  • 4
    One line using duration: `Get-History -Count 1 | %{ $_.Duration }` – makman99 Jul 12 '21 at 18:42
  • Based on @Phil's one-liner, here's a version that doesn't create a variable: `(Get-History -Count 1 | Select-Object -Property @{Name = 'ts'; Expression = { $_.EndExecutionTime - $_.StartExecutionTime} }).ts.ToString()` – zumalifeguard Jul 15 '22 at 19:36
120

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.

thegreendroid
  • 3,239
  • 6
  • 31
  • 40
Droj
  • 3,541
  • 3
  • 26
  • 19
  • I think you mean `Measure-Command { – Peter McEvoy Jun 13 '18 at 08:24
  • 2
    @Peter - it needs to be inside the block, otherwise Measure-Command consumes the output before it goes to the console. – Droj Jun 13 '18 at 14:41
  • My bad - I misunderstood what you were trying to do: see the output of the commands. For me, I wanted to see the *timings*, so needed to pipe the output of Measure-Command to see them – Peter McEvoy Jun 14 '18 at 13:51
  • 1
    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.... – Droj Jun 14 '18 at 19:35
  • 2
    Is Out-Default maybe better than Out-Host because compatible with scripting? https://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/ – MarcH Jan 04 '19 at 21:49
  • @MarcH, if you are doing this in a script, then that would probably make sense. – Droj Jan 07 '19 at 15:40
  • 1
    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) – MarcH Jan 07 '19 at 23:27
28

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

Mike West
  • 389
  • 3
  • 2
  • 2
    `Measure-Command` hides command output, so this solution is sometimes better. – codekaizen Oct 20 '18 at 04:39
  • 1
    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. – Raúl Salinas-Monteagudo May 22 '19 at 07:18
  • I'm using this to measure function calls. However you better add the arguments as well to the calling convention, like so: `& $block @Args`. – ikaerom Nov 04 '21 at 21:20
8

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

codewario
  • 19,553
  • 20
  • 90
  • 159
  • The question was "Timing a command's execution in PowerShell". What does that have to do with timing a process using Unix? – Jean-Claude DuBois Sep 08 '17 at 22:19
  • 6
    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". – codewario Sep 14 '17 at 17:07
7

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.

5

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 }
}
John Freeman
  • 2,552
  • 1
  • 27
  • 34
4

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 }
Kiquenet
  • 14,494
  • 35
  • 148
  • 243
3
(measure-commmand{your command}).totalseconds

for instance

(measure-commmand{.\do_something.ps1}).totalseconds
Sled
  • 18,541
  • 27
  • 119
  • 168
JamesNEW
  • 117
  • 7
-1

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

iRon
  • 20,463
  • 10
  • 53
  • 79
  • 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](https://stackoverflow.com/q/59889903/1701026) – iRon Jan 27 '20 at 10:23