Timing a command's execution in PowerShell

PowershellTimePerformance Testing

Powershell Problem Overview


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

Powershell Solutions


Solution 1 - Powershell

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

Solution 2 - Powershell

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

Solution 3 - Powershell

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.

Solution 4 - Powershell

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

Solution 5 - Powershell

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

Solution 6 - Powershell

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 }

Solution 7 - Powershell

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

Solution 8 - Powershell

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.

Solution 9 - Powershell

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPaolo TedescoView Question on Stackoverflow
Solution 1 - PowershellKeith HillView Answer on Stackoverflow
Solution 2 - PowershellShay LevyView Answer on Stackoverflow
Solution 3 - PowershellDrojView Answer on Stackoverflow
Solution 4 - PowershellMike WestView Answer on Stackoverflow
Solution 5 - PowershellBender the GreatestView Answer on Stackoverflow
Solution 6 - PowershellKiquenetView Answer on Stackoverflow
Solution 7 - PowershellJohn FreemanView Answer on Stackoverflow
Solution 8 - PowershellMaxCreatesCodeView Answer on Stackoverflow
Solution 9 - PowershelliRonView Answer on Stackoverflow