[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[技术讨论] PowerShell脚本块的递归性能检验

本帖最后由 Nsqs 于 2023-10-21 06:51 编辑

第一段 用scriptblock测试
  1. using namespace System.Collections
  2. using namespace System.Collections.Generic
  3. using namespace System.Diagnostics
  4. [Stopwatch]$sw=@{}
  5. [List[int]]$ll=@{}
  6. function m($a){
  7. [List[int]]$l=@{}
  8. [scriptblock]$fn={param($x)$l.Add($x);if($x -eq 1){return $l}else{$x--}$fn.Invoke($x)}
  9. $fn.Invoke($a)
  10. }
  11. function fn($x){$ll.Add($x);if($x -eq 1){return $ll}else{$x--}fn $x}
  12. $sw.Start()
  13. $a=1..1000|%{$x=m 10}
  14. $t=$sw.Elapsed.TotalSeconds.ToString('0.00s')
  15. $ll.Clear()
  16. $sw.Restart()
  17. $b=1..1000|%{$x=fn 10}
  18. $t;$sw.Elapsed.TotalSeconds.ToString('0.00s')
  19. $sw.Stop()
复制代码
第二段 用func测试
  1. using namespace System.Collections
  2. using namespace System.Collections.Generic
  3. using namespace System.Diagnostics
  4. [Stopwatch]$sw=@{}
  5. [List[int]]$ll=@{}
  6. function m($a){
  7. [List[int]]$l=@{}
  8. [func[int,IList]]$fn={param($x)$l.Add($x);if($x -eq 1){return $l}else{$x--}$fn.Invoke($x)}
  9. $fn.Invoke($a)
  10. }
  11. function fn($x){$ll.Add($x);if($x -eq 1){return $ll}else{$x--}fn $x}
  12. $sw.Start()
  13. $a=1..1000|%{$x=m 10}
  14. $t=$sw.Elapsed.TotalSeconds.ToString('0.00s')
  15. $ll.Clear()
  16. $sw.Restart()
  17. $b=1..1000|%{$x=fn 10}
  18. $t;$sw.Elapsed.TotalSeconds.ToString('0.00s')
  19. $sw.Stop()
复制代码
第三段 用action测试
  1. using namespace System.Collections
  2. using namespace System.Collections.Generic
  3. using namespace System.Diagnostics
  4. [Stopwatch]$sw=@{}
  5. [List[int]]$ll=@{}
  6. function m($a){
  7. [List[int]]$l=@{}
  8. [scriptblock]$fn={param($x)$l.Add($x);if($x -eq 1){$global:res=$l;return}else{$x--}$fn.Invoke($x)}
  9. $fn.Invoke($a)
  10. }
  11. function fn($x){$ll.Add($x);if($x -eq 1){return $ll}else{$x--}fn $x}
  12. $sw.Start()
  13. $a=1..1000|%{m 10;$x=$res}
  14. $t=$sw.Elapsed.TotalSeconds.ToString('0.00s')
  15. $ll.Clear()
  16. $sw.Restart()
  17. $b=1..1000|%{$x=fn 10}
  18. $t;$sw.Elapsed.TotalSeconds.ToString('0.00s')
  19. $sw.Stop()
复制代码
结果就不公布了,每台电脑计算力不一样,自己去体会

可以肯定一点的是性能这一块function,只能算是个中庸吧,玩递归的话,还是慎重考虑

忘记说了是在5.1下测试的,懒得再弄了,休息,如果有人用高版本测试结果发现速度更快的话,可以说一声,欢迎共同学习和进步

TOP

谢谢分享了
我是小白,希望老师多多帮助

TOP

本帖最后由 Nsqs 于 2023-10-21 07:49 编辑

重新修改了一下,感觉这样对比更加有说服力一点了

第一轮对比scriptblock与function
0.36s
1.18s

第二轮func与function
1.18s
1.16s

第三轮action与function
0.34s
1.19s

func委托函数套在函数里与直接用func委托的测试:
  1. using namespace System.Collections
  2. using namespace System.Collections.Generic
  3. using namespace System.Diagnostics
  4. function func($a){
  5. [List[int]]$l=@{}
  6. [func[int,IList]]$fn={param($x)$l.Add($x);if($x -eq 1){return $l}else{$x--}$fn.Invoke($x)}
  7. $fn.Invoke($a)
  8. }
  9. [List[int]]$la=@{}
  10. [func[int,ilist]]$fn={param($x)$la.Add($x);if($x -eq 1){return $l}else{$x--}$fn.Invoke($x)}
  11. [Stopwatch]$sw=@{}
  12. $sw.Start()
  13. 1..1000|%{$x=func 10}
  14. $t=$sw.Elapsed.TotalSeconds.ToString('0.00s')
  15. $sw.Restart()
  16. 1..1000|%{$la=@{};$r=$fn.Invoke(10)}
  17. $t;$sw.Elapsed.TotalSeconds.ToString('0.00s')
  18. $sw.Stop()
复制代码
运行结果:
1.16s
0.32s

也就是说如果你想写递归的话,性能方面写直接单写一个function和function+func是一样的如果在function里再写func
既然速度是一样的就不如直接写func来的快

想偷懒又想在function里写函数用 $scriptblock=#{脚本块} 能节省一些代码量,可以不用声明

这是AI的回答:
在 PowerShell 中,闭包是指一个函数可以访问在其定义之外定义的变量。当您在函数内部调用 func 时,
每次调用都会创建一个新的闭包对象,该闭包对象包含了函数定义时所捕获的变量。这样会导致每次调用 func 时都需要创建新的闭包对象。

根据AI的回答,大概是因为scriptblock是PowerShell独有的方法
套在函数内不需要额外创建新的委托因此function里写func委托函数每次调用 function 都会重新创建一个委托函数
scriptblock则不用..所以直接调用$fn.Invoke()效率会比套在function里的$fn速度更快

我这个测试结果是在ise内测试的,如果双击运行脚本速度只能更快

TOP

本帖最后由 Nsqs 于 2023-10-21 07:56 编辑

测试一下套在function里的scriptblock与直接调用scriptblock的递归测试
  1. using namespace System.Collections
  2. using namespace System.Collections.Generic
  3. using namespace System.Diagnostics
  4. function func($a){
  5. [List[int]]$l=@{}
  6. [scriptblock]$fn={param($x)$l.Add($x);if($x -eq 1){return $l}else{$x--}$fn.Invoke($x)}
  7. $fn.Invoke($a)
  8. }
  9. [List[int]]$lc=@{}
  10. [scriptblock]$fn={param($x)$lc.Add($x);if($x -eq 1){return $l}else{$x--}$fn.Invoke($x)}
  11. [Stopwatch]$sw=@{}
  12. $sw.Start()
  13. 1..1000|%{$x=func 10}
  14. $t=$sw.Elapsed.TotalSeconds.ToString('0.00s')
  15. $sw.Restart()
  16. 1..1000|%{$la=@{};$r=$fn.Invoke(10)}
  17. $t;$sw.Elapsed.TotalSeconds.ToString('0.00s')
  18. $sw.Stop()
复制代码
运行结果:
0.36s
0.28s

这两速度基本可以忽略不计
递归性能最差的应当就是直接用function了感觉

TOP

回复 5# Nsqs


    感谢分享

还有就是递归的深度影响很大,10层看不出来,100,200,1000层的耗时不是线性增加的

TOP

本帖最后由 Nsqs 于 2023-10-21 11:48 编辑

回复 6# wanghan519


    结合自己情况来用吧,这个测试至少证明写递归直接在函数里定义scriptblock是最方便的,不需要提前声明泛类型委托函数
如果直接使用PowerShell自带的Function去写递归性能就不怎么样

TOP

返回列表