假设我有一个这样的课程:
class ItemLimits
{
public string strAccountNumber = string.Empty;
public string strUserCode = string.Empty;
public long lAccumulatedAmount = 0L;
}
我有一个包含50000个元素的数组。
我还有一个50000项(或多或少)的数据集,我需要在ItemLimits
数组中找到与此数据集匹配的元素。
目前正在通过数据集循环完成此操作:
ItemLimits ilItemLimit = itemlimits.Where(s => s.strUserCode.Equals(dataset[i].User_UserCode, StringComparison.CurrentCultureIgnoreCase)
&& s.strAccountNumber.Equals(strUnEditedHomingAccountNo, StringComparison.CurrentCultureIgnoreCase)).First();
strUnEditedHomingAccountNo
是从之前的数据集中提取的。
找到我需要的ItemLimit
后,我需要添加到lAccumulatedAmount
。
我从性能基准测试中看到的是,循环开始时速度非常快,但随着时间的推移会变慢。这是一个线性减速,你可以在我做的图表中看到:
当我达到约40000件物品时,每件物品需要约40毫秒才能完成。这在我的脑海中是有道理的,因为我认为它只是逐个遍历项目,直到找到匹配,这显然是非常慢的大量项目。
数组和数据集中的项目数可能差异很大。
我考虑过尝试对数组进行排序并执行Array.BinarySearch
,但我不知道如何最有效地对其进行排序,因为strUserCode
和strAccountNumber
都可以改变,我也无法预测数据集的顺序。
这是该程序中最慢的部分,这就是为什么我想尝试优化它(大约70%的时间用于完成此操作,并且还有很多其他的东西在继续)。
如果有人能给我一些关于我能做什么的指示,那将非常感激。
我正在使用.NET 3.5,我无法改变它。
答案 0 :(得分:1)
我完全放弃了Where
,并使用此比较器使用Array.BinarySearch
完成了它:
class ItemLimitsComparer : Comparer<ItemLimits>
{
public override int Compare(ItemLimits x, ItemLimits y)
{
if(Convert.ToInt32(x.strUserCode) < Convert.ToInt32(y.strUserCode))
{
return -1;
}
if(Convert.ToInt32(x.strUserCode) > Convert.ToInt32(y.strUserCode))
{
return 1;
}
if(Convert.ToInt32(x.strUserCode) == Convert.ToInt32(y.strUserCode))
{
if(Convert.ToInt64(x.strAccountNumber) < Convert.ToInt64(y.strAccountNumber))
{
return -1;
}
if(Convert.ToInt64(x.strAccountNumber) > Convert.ToInt64(y.strAccountNumber))
{
return 1;
}
if(Convert.ToInt64(x.strAccountNumber) == Convert.ToInt64(y.strAccountNumber))
{
return 0;
}
}
return 0;
}
}
(这是我第一次使用它,我怀疑我有一个潜伏在某处的错误)
Where
已被替换为:
int index = Array.BinarySearch(itlaCreditLimits, new ItemLimits { strUserCode = dataset[i].User_UserCode, strAccountNumber = strUnEditedHomingAccountNo }, new ItemLimitsComparer());
if(index < 0)
{
throw new Exception("Didn't find ItemLimit for UserCode = " + dataset.User_UserCode + " and account number " + strUnEditedHomingAccountNo);
}
ItemLimits ilItemLimit = itlaCreditLimits[index];
这使我从所有50k物品的15分钟缩短到25秒。
答案 1 :(得分:0)
我认为你对导致这个问题的原因是正确的,我建议你使用快速查找的东西来加快速度。像字典一样,它非常快。你已经知道如何比较,看看你是否有正确的记录,你也只是寻找一个匹配,或者第一个......
尝试这样的事情,您需要更改DataSet的类型(不知道您使用的是什么),并且可能决定在制作itemLimitDictionary
时如何处理键碰撞,但除此之外应该加快速度:
public void DoTheNeedfull(ItemLimit[] itemLimits, DataSet dataSet)
{
var itemLimitDictionary = itemLimits.ToDictionary(i => MakeKey(i.One, i.Two), i => i);
for(var i = 0; i < dataSet.Count; i++)
{
var strUnEditedHomingAccountNo = BlackMagicMethod(dataSet[i]);
var key = MakeKey(dataSet[i].User_UserCode, strUnEditedHomingAccountNo);
if(itemLimitDictionary.ContainsKey(key))
{
// do your thing
}
else
{
// maybe this is an error
}
}
}
private string MakeKey(string keyPartOne, string keyPartTwo)
{
return keyPartOne.ToUpperInvariant() + keyPartTwo.ToUpperInvariant();
}
答案 2 :(得分:0)
据我了解,您希望迭代数据集,而不是增加计数器。 计数器特定于数据集条目的某些属性。
所以这听起来是GroupJoin()
linq声明的工作。有了它,您将获得一个ItemLimits对象,其中所有匹配的数据集项为IEnumerable<DataSetItem>
。然后,您可以在此内部枚举上调用Aggregate()
,并将此信息写入给定的ItemLimits
对象。