我如何加快这个?

时间:2014-09-23 12:36:21

标签: c# arrays linq collections .net-3.5

假设我有一个这样的课程:

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

我从性能基准测试中看到的是,循环开始时速度非常快,但随着时间的推移会变慢。这是一个线性减速,你可以在我做的图表中看到: time per item in ms

当我达到约40000件物品时,每件物品需要约40毫秒才能完成。这在我的脑海中是有道理的,因为我认为它只是逐个遍历项目,直到找到匹配,这显然是非常慢的大量项目。

数组和数据集中的项目数可能差异很大。

我考虑过尝试对数组进行排序并执行Array.BinarySearch,但我不知道如何最有效地对其进行排序,因为strUserCodestrAccountNumber都可以改变,我也无法预测数据集的顺序。

这是该程序中最慢的部分,这就是为什么我想尝试优化它(大约70%的时间用于完成此操作,并且还有很多其他的东西在继续)。

如果有人能给我一些关于我能做什么的指示,那将非常感激。

我正在使用.NET 3.5,我无法改变它。

3 个答案:

答案 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对象。