在 PowerShell 中计时命令的执行

发布于 2024-09-15 02:24:57 字数 245 浏览 7 评论 0原文

有没有一种简单的方法可以在 PowerShell 中计时命令的执行时间,例如 Linux 中的“time”命令?
我想出了这个:

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

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

time .\do_something.ps1

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

ぇ气 2024-09-22 02:24:58

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

斯约梅尔软件

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

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

例如,使用列出的性能测量命令,语言集成查询 (LINQ)(1) 通常被认为是完成某件事的快速方法,而且通常是这样,但当然并非总是如此!与本机 PowerShell 命令相比,任何测量到速度提高了 40 倍或以上的人都可能错误地测量或得出错误的结论。

关键是一些 .Net 类(如 LINQ)使用 延迟评估< /a> (也称为延迟执行(2))。这意味着当将表达式分配给变量时,它几乎立即看起来已完成,但实际上它还没有处理任何内容!

假设您 dot-source 你的<代码>。 ,它具有 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

.\Dosomething.ps1命令 结果很明显,后面的 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 的属性花费了大约90 倍的时间对象,然后是 $PowerShell 对象,而那只是一个对象!

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

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

(1) 有关 PowerShell 和 LINQ 的更多背景和示例,我推荐这个站点:使用 LINQ 的高性能 PowerShell
(2) 我认为这两个概念之间存在细微差别,因为延迟计算的结果是在需要时计算的,与延迟执行< /strong>结果是在系统空闲时计算的

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

故事↓在人 2024-09-22 02:24:57

是的。

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

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
π浅易 2024-09-22 02:24:57

您还可以从历史记录中获取最后一个命令,并从其 StartExecutionTime 中减去其 EndExecutionTime

.\do_something.ps1  
$command = Get-History -Count 1  
$command.EndExecutionTime - $command.StartExecutionTime

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
ぶ宁プ宁ぶ 2024-09-22 02:24:57

使用 Measure-Command

示例

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

通过到 Out-Host 的管道,您可以查看命令的输出,即
否则由 Measure-Command 消耗。

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.

鸠魁 2024-09-22 02:24:57

简单

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

然后可以使用

time { .\some_command }

你可能想要调整输出

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

新一帅帅 2024-09-22 02:24:57

这是我编写的一个函数,其工作方式与 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

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

又爬满兰若 2024-09-22 02:24:57

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

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

Measure-Command 返回 TimeSpan 对象。

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

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.

蛮可爱 2024-09-22 02:24:57

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

Function time() {
  $command = $args -join ' '
  Measure-Command { Invoke-Expression $command | Out-Default }
}

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 }
}
≈。彩虹 2024-09-22 02:24:57

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

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 }

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 }
秋意浓 2024-09-22 02:24:57
(measure-commmand{your command}).totalseconds

例如

(measure-commmand{.\do_something.ps1}).totalseconds
(measure-commmand{your command}).totalseconds

for instance

(measure-commmand{.\do_something.ps1}).totalseconds
念﹏祤嫣 2024-09-22 02:24:57

在 PowerShell 中创建一个 time 命令,用于测量其他命令的执行时间

1- 通过运行以下命令打开您的 PowerShell 配置文件:

notepad $PROFILE

2- 如果该配置文件不存在,使用以下方法创建一个:

New-Item -Type File -Force $PROFILE

3- 将以下函数添加到您的 PowerShell 配置文件中:

function global:time {
    $scriptBlock = [scriptblock]::Create($args -join ' ')
    Measure-Command -Expression $scriptBlock
}

4- 保存更改并关闭文件。

现在,时间< /code> 命令在任何新的 PowerShell 会话中全局可用

time Start-Sleep -Seconds 5

输出:

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 5
Milliseconds      : 10
Ticks             : 50108167
TotalDays         : 5.79955636574074E-05
TotalHours        : 0.00139189352777778
TotalMinutes      : 0.0835136116666667
TotalSeconds      : 5.0108167
TotalMilliseconds : 5010.8167

Create a time command in PowerShell that measures the execution time of other commands

1- Open your PowerShell profile by running:

notepad $PROFILE

2- If the profile doesn't exist, create one using:

New-Item -Type File -Force $PROFILE

3- Add the following function to your PowerShell profile:

function global:time {
    $scriptBlock = [scriptblock]::Create($args -join ' ')
    Measure-Command -Expression $scriptBlock
}

4- Save the changes and close the file.

Now, time command is available globally within any new PowerShell sessions

time Start-Sleep -Seconds 5

OUTPUT:

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 5
Milliseconds      : 10
Ticks             : 50108167
TotalDays         : 5.79955636574074E-05
TotalHours        : 0.00139189352777778
TotalMinutes      : 0.0835136116666667
TotalSeconds      : 5.0108167
TotalMilliseconds : 5010.8167
離人涙 2024-09-22 02:24:57

使用

  • Measure-Command 测量命令持续时间
  • 管道到 Out-Default 将命令输出到控制台
  • (interval).ToString() 以显示时间通用格式
  • 如果你希望它是一个别名:
    • $args -join ' 'Invoke-Expression 运行 arg
    • (可选)将函数添加到 code $profile 中以永久添加别名
    • (可选)"`"a 在命令完成时发出蜂鸣声
      默认 "c"
# to beep when the command is complete (optional)
function beep {
    # echo "`a" doesn't work in VSCode so I use
    # rundll32 user32.dll,MessageBeep
    echo "`a"
}
function time() {
    $command = $args -join ' '
    (Measure-Command { Invoke-Expression $command | Out-Default }).ToString()
    beep
}
> time sleep 1.23456
00:00:01.2374460

Use

  • Measure-Command to measure command duration
  • pipe to Out-Default to output command to console
  • (interval).ToString() to show time in common format
  • If you want it to be an alias:
    • $args -join ' ' and Invoke-Expression to run the arg
    • (optional) add the function to code $profile to add the alias permanently
    • (optional) "`"a to beep when the command is finished
      The default "c"
# to beep when the command is complete (optional)
function beep {
    # echo "`a" doesn't work in VSCode so I use
    # rundll32 user32.dll,MessageBeep
    echo "`a"
}
function time() {
    $command = $args -join ' '
    (Measure-Command { Invoke-Expression $command | Out-Default }).ToString()
    beep
}
> time sleep 1.23456
00:00:01.2374460
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文