背景/背景:
为了识别“来自数据集A的项目X与来自数据集B的项目Y或Z匹配”,我必须交叉检查/比较多个数据集(这些数据集倾向于相互不一致)。
这些涉及的数据集有些大(10万条记录),并且涉及我戳SQL数据库。
经过一些初步的研究和性能测试,我已经从通过“大规模数组”解析到有效地使用“索引哈希表”作为关键属性点。
挑战:
一旦使用哈希表,使用哈希表的速度非常快……但是我的问题是有效地构建哈希表。感觉就像我“快到了”,但是不得不诉诸(相对)缓慢的方法(50,000条记录大约需要300-400秒)。
这是我现在要编制索引的基本数据(我从SQL中获得了不同的设备名称列表,以及该设备具有多少记录的计数):
DEVICENAME COUNTOF
========== ========
DEVICE_1 1
DEVICE_2 1
DEVICE_3 2
.... ...
DEVICE_49999 3
DEVICE_50000 1
当前解决方案:
我目前正在通过遍历结果集(我从SQL中退出作为结果集的数组)并为每个订单项使用“ .add”来构造哈希表。
所以只是一个简单的...
for ($i=0; $i -lt @($SQL_Results).CountOf; $i++) {
$MyIndexHash.Add( @($SQL_Results[$i]).DeviceName, @($SQL_Results[$i]).CountOf)
}
相对而言,这有点“慢”(前面提到的300-400秒用于构建50,000个订单项)。我CAN等待如果需要的话,但因为(在预感),我尝试这是“近即时”下面,逗它,有可能是一个更好的办法做到这一点(花了3秒左右)。
$MyIndexHash.Keys = $SQL_Results.DEVICENAME
但是,这仅填充哈希表的KEY,而不是相关值。而且我还没有找到一种有效实现以下目标的方法(将值从数组中直接分配给哈希表):
$MyIndexHash.Keys = ($SQL_Results.DEVICENAME, $SQL_Results.COUNTOF)
这是一个“纯粹的性能”问题-因为我需要做的其他一些比较将是80,000个和150,000个订单项。如果我必须“只是等待”通过遍历SQL结果数组的每一行来构造哈希表,就这样吧。
注意-我已经看过了-Powershell 2和.NET:针对超大哈希表进行优化吗?-但由于我有可变的数据集(很好-“未知但可能很大”)可以处理,所以我不确定是否可以/希望开始分解哈希表。
另外,哈希表中的LOOKUP(一旦填充)毕竟是超快的……只是希望以某种更有效的方式完成哈希表的构建?
任何有关如何改善如何更有效地构建哈希表的建议都将受到欢迎。
谢谢!
更新/调查
基于对@Pawel_Dyl应该多快进行哈希表分配的评论,我让我研究了我的代码的变体和更大的(200k行项目)数据值集。
这是测试结果以及持续时间:
#Create the Demo Data... 200k lines
$Src = 1..200000 | % { [pscustomobject]@{Name="Item_$_"; CountOf=$_} }
# Test # 1 - Checking (... -lt $Src.Count) option vs (... -lt @($Src)Count ) ...
# Test 1A - using $Src.CountOf
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash1A = @{}
foreach ($i in $Src) { $hash1A[$i.Name] = $i.CountOf }
$Timer.Stop()
$Timer.ElapsedMilliseconds
# Duration = 736 ms
# Now with @()
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash1B = @{}
foreach ($i in @($Src)) { $hash1B[$i.Name] = $i.CountOf }
$Timer.Stop()
$Timer.ElapsedMilliseconds
# Duration = 728 ms
##################
# Test # 2 - Checking (... -lt $Src.Count) option vs (... -lt @($Src).Count ) ...
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash2A = @{}
for ($i=0; $i -lt @($Src).Count; $i++) {
$hash2A.Add(@($Src[$i]).Name, @($Src[$i]).CountOf)
}
$Timer.Stop()
$Timer.ElapsedMilliseconds
# Duration == 4,625,755 (!) (commas added for easier readability!
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash2B = @{}
for ($i=0; $i -lt $Src.Count; $i++) {
$hash2B.Add( $Src[$i].Name, $Src[$i].CountOf )
}
$Timer.Stop()
$Timer.ElapsedMilliseconds
# Duration == 1788 ms
因此,问题出在使用@()-s在循环中引用数组。旨在防止SQL中的1行数组/结果(出于某种奇怪的原因,Powershell并没有将其作为一个概念,而是将其完全视为DATAOBJECT而不是数组(因此,.Count之类的东西不可用)而不强制POSH通过@()将其作为数组处理。
因此,“暂时”的解决方案是添加一个简单的... If(@($ MyArray).Count -eq 1){用@()来做}} ElseIf(@($ MyArray).Count -gt 1){不使用@()-s来做事情
罪魁祸首-在循环中使用@()-s花费了将近1.25个小时,而同一操作则花费了约1秒钟。
进行更改会极大地加速工作(即使只用0.1秒就可以构造每个哈希表,即使90,000个以上的对象在“愤怒中”也是如此。代码使用起来不太方便,但是,哦,我仍然不明白为什么) Powershell在“ 1行数组”的概念上存在问题,因此决定将它们作为单独的数据类型以不同的方式进行处理,但是您就可以了。
我仍将查看DataReader的建议,以了解在哪里/如何在代码中最好地利用它们,以作为将来的改进。非常感谢您的所有建议和能使一切变得有意义的出色解释!
我希望性能瓶颈在哈希表之外。我测量了我见过的最常用的方法,结果如下:
#demo data
#$src = 1..200000 | % { [pscustomobject]@{Name="Item_$_";Count=$_} }
#1
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash1 = @{}
$src | % {$hash1[$_.Name]=$_.Count}
$timer.Stop()
$timer.ElapsedMilliseconds
#2
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash2 = @{}
for ($i=0; $i -lt $src.Count; $i++) {
$hash2.Add($src[$i].Name,$src[$i].Count)
}
$timer.Stop()
$timer.ElapsedMilliseconds
#3
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash3 = @{}
foreach ($i in $src.GetEnumerator()) { $hash3[$i.Name] = $i.Count }
$timer.Stop()
$timer.ElapsedMilliseconds
#4
$timer = [System.Diagnostics.Stopwatch]::StartNew()
$hash3 = @{}
foreach ($i in $src) { $hash3[$i.Name] = $i.Count }
$timer.Stop()
$timer.ElapsedMilliseconds
在我的计算机上分别花费了大约5s,〜1.7s,〜0.7s,〜0.7s来完成第1-4节(200000条记录)。如果必须进一步优化,我将评估一些用于构建字典的本机.NET方法。
尝试优化其余代码。提示:
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句