264

是否有一种简单的方法来计时 PowerShell 中命令的执行,例如 Linux 中的“时间”命令?
我想出了这个:

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

但我想要一些更简单的东西,比如

time .\do_something.ps1
4

9 回答 9

425

是的。

Measure-Command { .\do_something.ps1 }

请注意,一个小缺点Measure-Command是您看不到任何stdout输出。

[更新,感谢@JasonMArcher] 您可以通过将命令输出通过管道传输到写入主机的某个命令行开关来解决此问题,例如Out-Default,它变为:

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

查看输出的另一种方法是使用 .NETStopwatch类,如下所示:

$sw = [Diagnostics.Stopwatch]::StartNew()
.\do_something.ps1
$sw.Stop()
$sw.Elapsed
于 2010-08-18T15:18:57.393 回答
203

您还可以从历史记录中获取最后一个命令,并EndExecutionTime从它的StartExecutionTime.

.\do_something.ps1  
$command = Get-History -Count 1  
$command.EndExecutionTime - $command.StartExecutionTime
于 2010-08-18T20:10:14.193 回答
111

采用Measure-Command

例子

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

管道Out-Host允许您查看命令的输出,否则将由Measure-Command.

于 2014-07-07T18:39:13.843 回答
26

简单的

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

然后可以用作

time { .\some_command }

您可能需要调整输出

于 2013-04-08T17:43:18.250 回答
8

这是我编写的一个函数,其工作方式类似于 Unixtime命令:

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

于 2017-01-02T22:32:09.553 回答
3

使用秒表并格式化经过时间:

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 }
于 2013-10-11T10:42:54.850 回答
3

到目前为止,所有答案都未能满足提问者(和我)通过简单地在命令行开头添加“时间”来为命令计时的愿望。相反,它们都需要将命令括在括号 ( {}) 中以构成块。这是一个更像time在 Unix 上工作的简短函数:

Function time() {
  $command = $args -join ' '
  Measure-Command { Invoke-Expression $command | Out-Default }
}
于 2021-09-04T18:57:54.067 回答
2

一种更受 PowerShell 启发的方式来访问您关心的属性的值:

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

因为Measure-Command返回一个TimeSpan对象。

注意:TimeSpan 对象也有 TotalMilliseconds 作为双精度(例如上面我的例子中的 4.7322 TotalMilliseconds),这可能对您有用。就像 TotalSeconds、TotalDays 等一样。

于 2021-10-12T03:49:42.960 回答
-2

从答案中提到的任何性能测量命令中得出(不正确的)结论。除了查看(自定义)函数或命令的裸调用时间之外,还应考虑许多陷阱。

Sjoemel 软件

“Sjoemelsoftware”被选为 2015 年荷兰语
Sjoemelen的意思是作弊,而sjoemelsoftware这个词是由于大众汽车排放丑闻而应运而生。官方定义是“用于影响测试结果的软件”。

就个人而言,我认为“ Sjoemelsoftware ”并非总是故意创建以欺骗测试结果,而是可能源于适应类似于测试用例的实际情况,如下所示。

例如,使用列出的性能测量命令Language Integrated Query (LINQ) (1)通常被认为完成某事的快速方法,而且通常是,但肯定不是总是如此!与原生 PowerShell 命令相比,任何测量速度增加40 倍或更多的人都可能错误地测量或得出错误的结论。

关键是某些 .Net 类(如 LINQ)使用惰性求值(也称为延迟执行(2))。这意味着当将表达式分配给变量时,它几乎立即完成,但实际上它还没有处理任何事情!

假设您点源您的. .\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

检索对象的属性比检索对象要多90 倍,而这只是一个对象!$Linq$PowerShell

另请注意另一个陷阱,如果您再次执行此操作,某些步骤可能会比以前更快,这是因为某些表达式已被缓存。

最重要的是,如果您想比较两个函数之间的性能,您需要在您的用例中实现它们,从一个新的 PowerShell 会话开始,并将您的结论基于完整解决方案的实际性能。

(1) 有关 PowerShell 和 LINQ 的更多背景和示例,我推荐 tihis 站点:High Performance PowerShell with LINQ
(2) 我认为这两个概念之间存在细微差别,因为懒惰评估结果是在需要时计算的,相对于延迟执行是在系统空闲时计算结果

于 2020-01-25T11:35:36.677 回答