Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Last active June 22, 2022 05:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Jaykul/d7f2e7bb7ef1febaeae62953a4ee868b to your computer and use it in GitHub Desktop.
Save Jaykul/d7f2e7bb7ef1febaeae62953a4ee868b to your computer and use it in GitHub Desktop.
We need a better Measure-Command which can show averages
function Measure-CommandEx {
<#
.SYNOPSIS
A from-scratch approach to timing command execution using Stopwatch
#>
[CmdletBinding()]
param(
# The command to test (accepts value from pipeline)
[Parameter(ValueFromPipeline)]
[ScriptBlock[]]$Command,
# The number of times to test the command (defaults to 30)
[Alias("Count")]
[Int]$Iterations = 30,
# By default, Measure-CommandEx adds 20% overhead to the number of runs, and then discards the top and bottom 10%
# If set, this disables that feature, so you can see the outliers
[Switch]$ShowOutliers,
# If set, writes progress messages
# This is generally useful for very long runs, so you know it's working...
# But it will slow down your runs if you have a very large number of very quick iterations
[switch]$Progress
)
begin {
$watch = [System.Diagnostics.Stopwatch]::new()
$watch.Start()
## Increase count by 20% so we can discard the top and bottom 10%
if (!$ShowOutliers) {
[int]$totalIterations = $iterations * 1.2
} else {
[int]$totalIterations = $iterations
}
}
end {
$watch.Stop()
}
process {
foreach ($c in $command) {
if (!$NoBuffer) { $Null = & $c }
$Measurements = for ($i=0; $i -lt $totalIterations; $i++) {
$Before = $watch.ElapsedTicks
$Null = &$c
$After = $watch.ElapsedTicks
$After - $Before
if ($Progress) {
Write-Progress -Id 1 -Activity $c -CurrentOperation "Iteration $i - $([Timespan]::FromTicks($After - $Before))" -PercentComplete $(100 * ($i / $totalIterations))
}
}
if ($Progress) {
Write-Progress -Id 1 -Activity $c -Completed
}
if (!$ShowOutliers) {
$Measurements = $Measurements | Sort-Object | Select-Object -Skip ([int]($Iterations * 0.1)) -First $Iterations
}
$Measurements |
Measure-Object -Average -Minimum -Maximum |
&{ process{
[PSCustomObject]@{
PSTypeName = "ExecutionPerformance"
Average = [Timespan]::FromTicks($_.Average)
Maximum = [Timespan]::FromTicks($_.Maximum)
Minimum = [Timespan]::FromTicks($_.Minimum)
Iterations = $Iterations
Command = "$c".Trim()
}
}
}
}
}
}
##############################################################################
##
## Measure-CommandPerformance
##
## From Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Measures the average time of a command, accounting for natural variability by
automatically ignoring the top and bottom ten percent.
.EXAMPLE
PS >Measure-CommandPerformance.ps1 { Start-Sleep -m 300 }
Count : 30
Average : 312.10155
(...)
#>
param(
## The command to measure
[Scriptblock] $Scriptblock,
## The number of times to measure the command's performance
[Alias("Count")]
[int] $Iterations = 30
)
Set-StrictMode -Version Latest
## Figure out how many extra iterations we need to account for the outliers
$buffer = [int] ($iterations * 0.1)
$totalIterations = $iterations + (2 * $buffer)
## Get the results
$results = 1..$totalIterations |
Foreach-Object { Measure-Command $scriptblock }
## Sort the results, and skip the outliers
$middleResults = $results | Sort TotalMilliseconds |
Select -Skip $buffer -First $iterations
## Show the average
$middleResults | Measure-Object -Average TotalMilliseconds
filter Measure-CommandRepeatedly {
<#
.SYNOPSIS
A wrapper for Measure-Command to run commands multiple times and report averages (avg, min, max)
#>
param(
# The script to measure
[Parameter(ValueFromPipeline)]
[ScriptBlock]$Expression,
# Number of iterations to run
## The number of times to measure the command's performance
[Alias("Count")]
[int] $Iterations = 30
)
@(1..$Iterations).ForEach{ Microsoft.PowerShell.Utility\Measure-Command $Expression } |
Measure-Object TotalMilliseconds -Average -Minimum -Maximum |
ForEach-Object {
[PSCustomObject]@{
PSTypeName = "ExecutionPerformance"
Average = $_.Average
Maximum = $_.Maximum
Minimum = $_.Minimum
Command = $Expression
Iterations = $_.Count
}
}
}
Update-TypeData -TypeName ExecutionPerformance -DefaultDisplayPropertySet Average, Maximum, Minimum, Command
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment