轻量级的PowerShell性能测试 2


PowerShell性能优化系列文章

  1. PowerShell优化和性能测试
  2. 让你的PowerShell For循环提速四倍
  3. 优化PowerShell的性能和内存消耗
  4. PowerShell提速和多线程
  5. 优化PowerShell脚本的几个小技巧
  6. 轻量级的PowerShell性能测试

当你写脚本的时候,可能经常会关心这几个问题:

  1. 我的脚本有没有实现预期的功能?
  2. 我的脚本能被别人看懂吗?
  3. 我的脚本效率高吗?

前一两个的答案是主观的,今天我来帮助大家找出测试脚本效率的方法。为了更精确,我会在PowerShell中使用不同的方式来做同一件事,然后分别计算它们的效率。

看PowerShell脚本的效率可能会非常棘手。在PowerShell中很多事可以用Cmdlet来做,也可以使用.NET中的方法来做,也可以使用Com对象,甚至使用cmd命令来做。同样的,使用PowerShell脚本语言来做一些事情,效果相同,但是性能可能稍有差异。

今天,我会找出一个可以使用许多方式完成的场景,并且会向大家展示可以使用Get-History和measure-Command这两条命令,对你刚才运行的命令,做一个简单的性能测试,以方便优化你的脚本。

这个场景够常见了:在一个ForeEach中,你想调用每一个的成员的某个方法,但是你还想把每一个成员在foreach返回中放进一个集合。但是为了防止你调用的方法的输出结果也被添加集合中,所以你可能需要抛弃这些输出结果(抑制输出)。

主要有三种方式去丢弃一个方法或者一个命令的结果:把值赋给一个$null,把它转换成[void],或者使用管道把它发送给Out-Null去调用。下面列出这几个方法:

$mycollection | foreach { $null = $_.MyMethod() ; $_ } | <其它调用>
$mycollection | foreach { [void]$_.MyMethod() ; $_ } | <其它调用>
$mycollection | foreach { $_.MyMethod() | Out-Null ; $_ } | <其它调用>

我曾经建议大家使用Out-Null来完成这件事,但是有个同事出来挑战说,这是不是最快的方案。所以我决定,我要计算出到底哪种方案是最快的。

原来语言约束更加高效,可以让你非常安全的转换结果。结果的转换不会更改底层变量的约束,除非你在操作符的左边使用约束,所以:

# 转换方法的返回值
void]$_.MyMethod()

# 会转换方法的返回值
$a = [void]$_.MyMethod() 

# 会把变量a的约束变成void
[void]$a = $_.MyMethod()

就我个人而言,发现第三个例子有时候比较有用,但是潜藏着一些危险。理由来自我自己的经验(有一次,我直接复制粘贴了第三个代码,还没意识到危险)。有一个用完以后就扔掉的变量确实很实用(这也就是为什么存在$null),但是这样做偶尔很危险。

我会指出效率的差异,这里是我怎样计算出丢弃中间结果最好(可读性,最快)的方式:
为了工作的展开,我决定把同样的操作执行适当的次数:1000次,我的三个测试是:

$result = (1..1000 | % { $_.GetType() | Out-Null; $_})
$result = (1..1000 | % { [void]$_.GetType(); $_})
$result = (1..1000 | % { $null = $_.GetType(); $_})

然后我使用下面的代码来收集我刚才的操作:

$results =  (Get-History)[-1..-3]

使用一个称之为“负索引”的小转换,会收集我刚才跑的三段命令。在PowerShell中如果你把一个负数传递给列表,它会从集合的结尾开始往前索引。

现在如果你看历史数据,它会包含一个开始执行时间和结束执行时间。这也就意味着我可以通过查询历史数据来找出那个方式更快。这是从历史数据列表中看到的:

$results | % { $_.CommandLine ; 
($_.EndExecutionTime -$_.StartExecutionTime).TotalMilliseconds }

$result = (1..1000 | % { $null = $_.GetType(); $_})
218.4

$result = (1..1000 | % { [void]$_.GetType(); $_})
234

$result = (1..1000 | % { $_.GetType() | Out-Null; $_})
670.8

注意:也可以使用measure-command来这样度量一个表达式。除了从历史数据中查看命令,和命令的时间,也可以使用Measure-Command –Expresson {ScriptBlock}这样的结构来计算一段特定的PowerShell脚本到底运行了多久。

正如你看到的那样:Out-Null 是抑制输出最效率最低的方式。以16毫秒之差,最后的赢家是将代码的输出结果赋值给一个空变量$null(不同的测试机器,数值不同)。

所以最快的方式应当是:

$mycollection | foreach { $null = $_.MyMethod() ; $_ } | <其它调用>

希望把这当成一个教程,来帮助大家提供一个判断脚本执行效率的途径,以便找个更快更好的脚本习惯。

原文作者:James Brundage[MSFT]
原文链接:Lightweight Performance Testing with PowerShell

本文链接: https://www.pstips.net/lightweight-performance-testing-with-powershell.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!

关于 Mooser Lee

我是一个Powershell的爱好者,创建了PowerShell中文博客,热衷于Powershell技术的搜集和分享。本站部分内容来源于互联网,不足之处敬请谅解,并欢迎您批评指正。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

2 条评论 “轻量级的PowerShell性能测试