我正在尝试创建一个相当大的哈希表数组,其中许多数据是完全随机的或从列表中随机选择的。
这是我的当前代码
$ArrayData = @()
$ArrayDataRows = 150000
foreach ($i in 1..$ArrayDataRows) {
$thisobject = [PSCustomObject] @{
Number = $i
Place = Get-Random -InputObject NJ, UT, NY, MI, PA, FL, AL, NM, CA, OK, TX, CO, AZ
Color = Get-Random -InputObject red, yellow, blue, purple, green, white, black
Zone = (Get-Random -InputObject $([char[]](65..90)) -Count 10) -join ""
Group = Get-Random -InputObject @(1..20)
}
$ArrayData += $thisobject
}
我注意到的是,它似乎效率不高。完成15万行总共需要25分钟。
我这里未发布一些其他代码,该代码测量了每个实例花费了多长时间,并估计了每个实例到其先前实例的平均值。最初,它可以让我估计总共450秒,而对于前3k个项目,平均每个实例为0.002,但后来它一直缓慢地爬升到平均速度的0.016或8倍。
如何在获得相同数据的同时优化和/或提高效率?
答案 0 :(得分:2)
[编辑-您未在创建哈希表数组。您正在制作PSCustomObject
个项目的数组。 [*咧嘴*]]
标准数组是固定大小对象。请查看$ArrayData.IsFixedSize
进行确认。 [咧嘴]
因此,当您在标准阵列上使用+=
时,powershell会制作一个新的,单项较大的阵列,将旧的复制到新的阵列中,最后添加新的阵列。当项目数量和尺寸“很小”时,它很快,但是随着数量/尺寸的增加,它变得越来越慢[越来越慢,越来越慢]。
有两种常见的解决方案...
.Add()
方法的集合类型ArrayList
(不推荐使用)和Generic.List
是人们通常使用的工具。当添加到第一个时,它会输出一个索引号,因此即使它不被弃用,我也不会使用它。 [咧嘴] $Results = foreach ($Thing in $Collection) {Do-Stuff}
,脚本块的输出将保留在RAM中,直到循环完成。然后将其全部填充到$Results
集合中。 第二个是最快的。
如果构建后无需更改集合的大小,请使用第二种方法。否则使用第一个。
作为速度的示例,您的代码(包含15,000个项目)在我的系统上运行39秒。使用“发送到输出”技术需要24秒。
请记住,随着数组变大,速度将继续恶化。我不愿意等待15万次迭代。
这是我的演示代码...
$ArrayDataRows = 15e3
$PlaceList = 'NJ, UT, NY, MI, PA, FL, AL, NM, CA, OK, TX, CO, AZ'.Split(',').Trim()
$ColorList = 'red, yellow, blue, purple, green, white, black'.Split(',').Trim()
$UC_LetterList = [char[]](65..90)
$GroupList = 1..20
(Measure-Command -Expression {
$ArrayData = foreach ($i in 1..$ArrayDataRows) {
[PSCustomObject] @{
Number = $i
Place = Get-Random -InputObject $PlaceList
Color = Get-Random -InputObject $ColorList
Zone = -join (Get-Random -InputObject $UC_LetterList -Count 10)
Group = Get-Random -InputObject $GroupList
}
}
}).TotalMilliseconds
# total ms = 24,390
答案 1 :(得分:1)
Lee_Daily's helpful answer讨论了有关构建数组(集合)的重要常规优化技术。
这个难题的另一个重要方面是,如果可能,避免在循环内执行(多个)cmdlet调用。
使用Get-Random
(System.Random
)替换[random]
调用可提供最大的加速(PSv5 +语法):
$ArrayDataRows = 150000
$places = 'NJ', 'UT', 'NY', 'MI', 'PA', 'FL', 'AL', 'NM', 'CA', 'OK', 'TX', 'CO', 'AZ'
$colors = 'red', 'yellow', 'blue', 'purple', 'green', 'white', 'black'
$chars = [char[]] (65..90)
$nums = 1..20
# Instantiate a random number generator.
$rndGen = [random]::new()
$ArrayData = foreach ($i in 1..$ArrayDataRows) {
[PSCustomObject] @{
Number = $i
Place = $places[$rndGen.Next(0, $places.Count)]
Color = $colors[$rndGen.Next(0, $colors.Count)]
Zone = -join $(
$charList = [Collections.Generic.List[char]]::new($chars)
foreach ($n in 1..10) { $randIndex = $rndGen.next(0, $charList.count); $charList[$randIndex]; $charList.RemoveAt($randIndex) }
)
Group = $nums[$rndGen.Next(0, $nums.Count)]
}
在我的计算机上,以上操作大约需要12秒钟,而您的原始命令运行了大约35分钟(!),这相当于大约175的加速因子。
基准:
以下是与您的原始方法,Lee的优化版本以及上面基于[random]
的解决方案形成对比的示例时间安排;绝对数字并不重要,但相对性能却很重要,如Factor
列所示:
带有1000
数组元素:
Factor Secs (10-run avg.) Command
------ ------------------ -------
1.00 0.100 # with [random]…
12.78 1.273 # with Get-Random - optimized…
13.45 1.340 # with Get-Random - original approach…
请注意,在构建1000个元素时,对数组构建方法的优化提供了一定的但不是很大的加速,但是好处是,元素越多,越大。
带有10,000
数组元素:
Factor Secs (10-run avg.) Command
------ ------------------ -------
1.00 1.082 # with [random]…
12.29 13.296 # with Get-Random - optimized…
20.40 22.081 # with Get-Random - original approach…
凭借10,000个元素,对数组构建的优化已经获得了丰厚的回报。
我没有耐心运行150,000
元素,但是很容易修改以下使用Time-Command
function的代码:
$ArrayDataRows = 1000
$places = 'NJ', 'UT', 'NY', 'MI', 'PA', 'FL', 'AL', 'NM', 'CA', 'OK', 'TX', 'CO', 'AZ'
$colors = 'red', 'yellow', 'blue', 'purple', 'green', 'white', 'black'
$chars = [char[]] (65..90)
$nums = 1..20
Time-Command -Count 10 { # with [random]
# Instantiate a random number generator.
$rndGen = [random]::new()
$ArrayData = foreach ($i in 1..$ArrayDataRows) {
[PSCustomObject] @{
Number = $i
Place = $places[$rndGen.Next(0, $places.Count)]
Color = $colors[$rndGen.Next(0, $colors.Count)]
Zone = -join $(
$charList = [Collections.Generic.List[char]]::new($chars)
foreach ($n in 1..10) { $randIndex = $rndGen.next(0, $charList.count); $charList[$randIndex]; $charList.RemoveAt($randIndex) }
)
Group = $nums[$rndGen.Next(0, $nums.Count)]
}
}
}, { # with Get-Random - optimized
$ArrayData = foreach ($i in 1..$ArrayDataRows) {
[PSCustomObject] @{
Number = $i
Place = Get-Random -InputObject $places
Color = Get-Random -InputObject $colors
Zone = -join (Get-Random -InputObject $chars -Count 10)
Group = Get-Random -InputObject $nums
}
}
} ,{ # with Get-Random - original approach
$ArrayData = @()
foreach ($i in 1..$ArrayDataRows) {
$thisobject = [PSCustomObject] @{
Number = $i
Place = Get-Random -InputObject $places
Color = Get-Random -InputObject $colors
Zone = -join (Get-Random -InputObject $chars -Count 10)
Group = Get-Random -InputObject $nums
}
$ArrayData += $thisobject
}
}