欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

PowerShell脚本性能优化技巧总结

程序员文章站 2022-07-04 23:29:34
最终优化 在你写脚本时,不要老是想着去优化,因为你的部分优化代码可能最终被丢弃。而且老是想着优化也会降低你的生产效率,因为和cpu的时间相比,脚本编写者的时间可能会更宝贵...

最终优化

在你写脚本时,不要老是想着去优化,因为你的部分优化代码可能最终被丢弃。而且老是想着优化也会降低你的生产效率,因为和cpu的时间相比,脚本编写者的时间可能会更宝贵。

使用过滤参数

powershell可能会消耗很多资源,因为许多cmdlet本身的设计就是用来提供数目很大的数据。所以,如果你使用的cmdlet命令支持-filter, -include, 和 -exclude这样的过滤条件,就尽量使用它们。

首先如果一条命令支持-filter 过滤器参数,那就说明这里可能隐藏着一个对象访问api。使用过滤,可能会让代码执行得非常快,因为通常会在对象被创建之前就执行过滤。相反一条命令支持-include, 和 -exclude,这样的过滤会发生在对象被创建后,对象进入管道之前。所以后者的效率比-filter 要低。尽管如此,使用-include, 和 -exclude后,让部分对象不进入管道,速度也是非常快的。

有时,应当使用更多的类型去过滤。比如你想搜索d盘下所有的后缀名为htm的文件。我们应当会使用*.htm作为过滤-filter条件,powershell使用传统的文件系统通配符,仅会返回所有匹配到的文件对象。这样效率很高,因为这种简单模式匹配,windows api本身在底层就实现了。然而windows api本身也有许多限制,因为它太老了,它会忽略文件后缀名中除了htm这三个字符以外的所有字符。所以即使有html这样后缀名它也会返回。所以此时,我们需要-filter和-include双剑合璧,打败金轮法王。

复制代码 代码如下:
dir d: -filter '*.htm' -include '*.html' -recurse

但是有一点要记住,使用-filter过滤条件是很快,但是到底快到什么程度取决于-filter调用的底层api。我们来举个例子吧:
复制代码 代码如下:
get-wmiobject -class win32_product  -filter 'vendor like "%microsoft%"'

这个例子会查询机器安装的所有微软的产品,纵然我们使用了-filter,仍旧很慢?因为-filter调用的是windows management instrumentation (wmi)api,这个是基于wmi查询语言(wql)的,过滤是发生在wmi内部的。

减少资源的占用

性能的优化包括降低时间复杂度和空间复杂度,但是很多时候,鱼和熊掌不可兼得。你只能选择其一。举个例子吧:比如你想列出d盘下所有的文件,然后针对每个文件做点事情,你可能会使用foreach-object来变量整个集合中的文件系统对象:

复制代码 代码如下:
get-childitem -path d:\ -recurse | foreach-object { do-something }

使用了这个命令后,每一个文件对象经过额外的包装后,会经过管道的边界,代码的执行效率显著下降,但是它不会占用太多内存,因为每次只有一个对象在管道中流通。

另外一种方法,你可能会使用foreach循环:

复制代码 代码如下:
foreach($file in (get-childitem -path d:\ -recurse)){
 do-something }

这段代码执行起来非常快,因为它避免了管道边界。但是它在处理之前会把所有文件对象加入到集合中。所以如果这个集合非常庞大,它可能会像某国的某能源局领导占用上亿人民币一样,洪水禽兽般占用系统资源。

foreach循环比foreach-object,执行速度快,消耗的内存多。但是如果你能确定你要处理的数据规模不是很大,foreach循环当然是上上之选。

使用sleep降低cpu的使用率

接触很多对象的powershell脚本,通常会执行很长时间,对于处理器也不会怜香惜玉。现在单核cpu已成往事,在多核的今天这可能不是个事,但这仍旧可能会导致系统花费了大量时间去等待。如果你的脚本消耗了很多cpu周期,或者需要等待事情发生,你可以使用start-sleep这条命令来降低处理器的使用率。默认slee是按秒来等待的,这肯定受不了,你可以设置一个毫秒级别的暂停。始终的分辨率不超过10到20毫秒会更好(再小就没有意义了),所以你可以指定最小暂停时间为20毫秒。

另外你可能不想让你的脚本每个循环周期中都sleep,只让部分周期sleep,以给cpu腾出时间去做其它事。下面的脚本会借助操作符%取模来保证每10轮暂停一次:

复制代码 代码如下:
$i=0
get-childitem -recurse |
 foreach-object{    $i+=1
    if($i%10 –eq 0)
   {sleep -mill 20}
   do-something
}

简单的优化方案

你可以把这些小技巧归纳成一个统一的优化方案。首先,别急着优化直到脚本写完了。接着,使用filter过滤来减少限制返回对象的数目,然后再用-include和,-exclude进行微调,这样做既会减少运行时间,也会降低资源的使用率。基于这一点,如果你的数据量比较大,就使用foreach循环来代替foreach-object,这样会让你的脚本执行效率提高。当然如果你的数据规模到了10万级以上,这可能会引入新的性能问题。最后,如果你发现自己的脚本cpu占用率老高老高,看看能否在一些循环中使用start-sleep命令,来放松一下。