想知道在数组搜索之后是否有机会提高性能。目前是16秒。
Measure-Command -Expression {
$a = @()
$b = @()
1..10000 | %{$a += $_}
1..10000 | %{$b += $_}
#Try to resize but still running 16 seconds
[array]::Resize([ref]$a,10000)
[array]::Resize([ref]$b,10000)
foreach ($i in $a){
if ($b -contains $i) {
#write-host $i
}
}
}
+=
在数组的情况下重新创建整个数组:它将旧内容与新元素一起复制到新数组中。这可以说是在PowerShell中填充大型数组的最糟糕的方法。仅当
+=
是不在循环内的一次性操作,或者数组很小且时间无关紧要时,才使用 。直接填充数组:
$a = 1..10000
或者直接收集
foreach
语句的输出:$a = foreach ($i in 1..10000) { $i }
或者使用 ArrayList:
$a = [Collections.ArrayList]@() foreach ($i in 1..10000) { $a.Add($i) >$null }
请注意,在这种特殊情况下,将现有数组转换为 ArrayList 会更快:
$a = [Collections.ArrayList]@(1..10000)
流水线是一个复杂的操作,它比流控制语句(如
foreach
(语句,而不是 cmdlet)、while
、do
等流控制语句慢几倍/很多倍。与内部的简单代码相比,ScriptBlock(大括号中的代码
{ }
cmdletForEach
中的代码,别名为%
)需要花费大量时间来为每个元素创建执行上下文。-contains
检查每个元素,直到找到匹配项,因此最终的迭代次数在最坏的情况下$a.count * $b.count
或平均的一半。
最有效的方法是构建查找表:
作为哈希表:
$a = 1..10000 $b = 1..10000 $bIndex = @{} foreach ($i in $b) { $bIndex[$i] = $true } foreach ($i in $a) { if ($bIndex[$i]) { #write-host $i } }
在 i7 CPU 上为 15 毫秒
作为哈希集(.NET 3.5 及更高版本,自 Win7 起内置,可安装在 XP 上):
$a = 1..10000 $b = 1..10000 $bIndex = [Collections.Generic.HashSet[int]]$b foreach ($i in $a) { if ($bIndex.Contains($i)) { #write-host $i } }
在 i7 CPU 上为 7 毫秒
使用 HashSet 与数组相交,然后迭代结果:
$a = 1..10000 $b = 1..10000 $inBoth = [Collections.Generic.HashSet[int]]$a $inBoth.IntersectWith([Collections.Generic.HashSet[int]]$b) foreach ($i in $inBoth) { #write-host $i }
在 i7 CPU 上为 7 毫秒
如果数组的内容不同,即当交集比数组小得多时,它会快很多倍。
也可以构建一个真正的索引,其中包含数组中原始项目索引的列表 - 在重复值的情况下很有用。