是否有一种简单的方法来计时 PowerShell 中命令的执行,例如 Linux 中的“时间”命令?我想出了这个:
$s=Get-Date; .\do_something.ps1 ; $e=Get-Date; ($e - $s).TotalSeconds
但我想要一些更简单的东西,比如
time .\do_something.ps1
是的。
Measure-Command { .\do_something.ps1 }
请注意,Measure-Command
的一个小缺点是您看不到 stdout
输出。
[更新,感谢@JasonMArcher] 您可以通过将命令输出通过管道传输到一些写入主机的命令行开关来解决这个问题,例如 Out-Default
所以它变成:
Measure-Command { .\do_something.ps1 | Out-Default }
查看输出的另一种方法是使用 .NET Stopwatch
类,如下所示:
$sw = [Diagnostics.Stopwatch]::StartNew()
.\do_something.ps1
$sw.Stop()
$sw.Elapsed
您还可以从历史记录中获取最后一个命令,并从它的 StartExecutionTime
中减去它的 EndExecutionTime
。
.\do_something.ps1
$command = Get-History -Count 1
$command.EndExecutionTime - $command.StartExecutionTime
Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -desc
按一天中的小时查看您的 PowerShell 使用模式。 :-)
$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
使用Measure-Command
例子
Measure-Command { <your command here> | Out-Host }
到 Out-Host
的管道允许您查看命令的输出,否则 Measure-Command
会使用该输出。
Measure-Command {<your command } | Out-Host
- Out-Host 在脚本块之外
简单的
function time($block) {
$sw = [Diagnostics.Stopwatch]::StartNew()
&$block
$sw.Stop()
$sw.Elapsed
}
然后可以用作
time { .\some_command }
您可能需要调整输出
Measure-Command
隐藏命令输出,因此此解决方案有时更好。
& $block @Args
。
这是我编写的一个函数,其工作方式类似于 Unix time
命令:
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
}
}
来源:https://gist.github.com/bender-the-greatest/741f696d965ed9728dc6287bdd336874
Measure-Command
或您可以在 Powershell 中计时执行的各种其他方法之一。如果您阅读原始问题,他会要求一些“类似于 Linux 中的 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"
}
使用示例
StepTimeBlock ("Publish {0} Reports" -f $Script:ArrayReportsList.Count) {
$Script:ArrayReportsList | % { Publish-Report $WebServiceSSRSRDL $_ $CarpetaReports $CarpetaDataSources $Script:datasourceReport };
}
StepTimeBlock ("My Process") { .\do_something.ps1 }
到目前为止,所有答案都没有达到提问者(和我)通过简单地在命令行开头添加“时间”来为命令计时的愿望。相反,它们都需要将命令括在方括号 ({}
) 中以生成块。这是一个在 Unix 上更像 time
的简短函数:
Function time() {
$command = $args -join ' '
Measure-Command { Invoke-Expression $command | Out-Default }
}
一种更受 PowerShell 启发的方式来访问您关心的属性的值:
$myCommand = .\do_something.ps1
Measure-Command { Invoke-Expression $myCommand } | Select -ExpandProperty Milliseconds
4
因为 Measure-Command 返回一个 TimeSpan 对象。
注意:TimeSpan 对象也有 TotalMilliseconds 作为双精度(例如上面我的例子中的 4.7322 TotalMilliseconds),这可能对您有用。就像 TotalSeconds、TotalDays 等一样。
(measure-commmand{你的命令}).totalseconds
例如
(测量命令{.\do_something.ps1}).totalseconds
从答案中提到的任何性能测量命令中得出(不正确的)结论。除了查看(自定义)函数或命令的裸调用时间之外,还应考虑许多陷阱。
Sjoemel 软件
“Sjoemelsoftware”被选为 2015 年荷兰语 Sjoemelen 的意思是作弊,而 sjoemelsoftware 这个词是由于大众汽车排放丑闻而应运而生。官方定义是“用于影响测试结果的软件”。
就个人而言,我认为“Sjoemelsoftware”并非总是故意创建以欺骗测试结果,而是可能源于适应类似于测试用例的实际情况,如下所示。
例如,使用列出的性能测量命令 Language Integrated Query (LINQ)(1) 通常被认为是完成某事的禁食方法,而且通常是,但肯定是不总是!任何人如果测量与原生 PowerShell 命令相比 40 倍或更多 的速度增加,则可能是在错误地测量或得出错误的结论。
关键是某些 .Net 类(如 LINQ)使用 lazy evaluation(也称为延迟执行(2))。这意味着当将表达式分配给变量时,它几乎立即完成,但实际上它还没有处理任何事情!
假设您 dot-source 您的 . .\Dosomething.ps1
命令具有 PowerShell 或更复杂的 Linq 表达式(为了便于解释,我已将表达式直接嵌入到 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
结果看起来很明显,后面的 Linq 命令比第一个 PowerShell 命令快了大约 40 倍。不幸的是,事情并没有那么简单……
让我们显示结果:
PS C:\> $PowerShell
Index Property
----- --------
12345 104123841
PS C:\> $Linq
Index Property
----- --------
12345 104123841
正如预期的那样,结果是相同的,但如果您仔细观察,您会注意到显示 $Linq
结果比显示 $PowerShell
结果花费的时间要长得多。
让我们具体测量 通过检索结果对象的属性:
PS C:\> (Measure-Command {$PowerShell.Property}).totalmilliseconds
14.8798
PS C:\> (Measure-Command {$Linq.Property}).totalmilliseconds
1360.9435
检索 $Linq
对象的属性比检索 $PowerShell
对象的属性长了大约 90 倍,而这只是一个对象!
另请注意另一个陷阱,如果您再次执行此操作,某些步骤可能会比以前更快,这是因为某些表达式已被缓存。
最重要的是,如果您想比较两个函数之间的性能,您需要在您的用例中实现它们,从一个新的 PowerShell 会话开始,并将您的结论基于完整解决方案的实际性能。
(1) 有关 PowerShell 和 LINQ 的更多背景知识和示例,我推荐 tihis 站点:High Performance PowerShell with LINQ
(2) 我认为这两个概念之间存在细微差别惰性评估结果是在需要时计算的,与延迟执行是在系统空闲时计算结果 >
time { ping -n 1 google.com } -Samples 10
将运行命令10
次并返回平均、最短和最长所用时间。您可以添加-Silent
以吞下 STDOUT。$t = Measure-Command {<<your command or code block>>}
。试一试,然后在提示符处键入$t
以查看您的结果以及您有权访问的所有属性,例如$t.Milliseconds
、$t.TotalSeconds
等。然后我们可以写入我们想要的任何输出,例如,{ 5}| Out-Default
类似,您也可以使用-OutVariable
来存储输出对象,例如$m = Measure-Command { Get-WinEvent -LogName System -MaxEvents 1 -OutVariable events }; $events.Message; $m.TotalSeconds