Board logo

标题: [技术讨论] PowerShell脚本块的递归性能检验 [打印本页]

作者: Nsqs    时间: 2023-10-15 18:41     标题: 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,只能算是个中庸吧,玩递归的话,还是慎重考虑
作者: Nsqs    时间: 2023-10-15 18:45

忘记说了是在5.1下测试的,懒得再弄了,休息,如果有人用高版本测试结果发现速度更快的话,可以说一声,欢迎共同学习和进步
作者: yyz219    时间: 2023-10-15 19:49

谢谢分享了
作者: Nsqs    时间: 2023-10-21 07:00

本帖最后由 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内测试的,如果双击运行脚本速度只能更快
作者: Nsqs    时间: 2023-10-21 07:51

本帖最后由 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了感觉
作者: wanghan519    时间: 2023-10-21 11:29

回复 5# Nsqs


    感谢分享

还有就是递归的深度影响很大,10层看不出来,100,200,1000层的耗时不是线性增加的
作者: Nsqs    时间: 2023-10-21 11:44

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

回复 6# wanghan519


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




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2