【问题标题】:How do I speed up this Where? [closed]我如何加快这个在哪里? [关闭]
【发布时间】:2014-11-17 16:06:22
【问题描述】:

假设我有这样的课程:

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,但我不知道如何最有效地订购它,因为strUserCodestrAccountNumber 都可以更改,我可以' t 预测数据集的顺序。

这是程序中最慢的部分,这也是我想尝试优化它的原因(大约 70% 的时间都花在了这样做上,还有很多其他事情要做)。

如果有人能给我一些关于我能做什么的建议,将不胜感激。

我使用的是 .NET 3.5,但无法更改。

【问题讨论】:

  • 这个问题似乎是题外话,因为它属于codereview.stackexchange.com
  • 糟糕。是否可以迁移问题,或者我应该删除它并去那里问?
  • 我不知道,也许版主可以做到。
  • 不如先将 ItemLimits 存储在 List 中,然后在将其链接到匹配的数据记录后将其删除?
  • @Zache ItemLimits 可以多次使用。他们必须留在那里。

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


【解决方案1】:

我认为您对导致此问题的原因是正确的,我建议您使用具有快速查找功能的东西来加快速度。像字典一样的东西,速度非常快。你已经知道如何比较,看看你是否有正确的记录,你也只寻找一个匹配,或者第一个......

尝试这样的事情,您需要更改 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();
}

【讨论】:

  • 我尝试使用字典(我应该提到这一点),但由于某种原因它不允许我编辑 Value 部分。也大声笑@BlackMagicMethod。我实际上已经找到了一种进行二进制搜索的方法,并且使用它在我的机器上从 15 分钟缩短到了 25 秒。
【解决方案2】:

据我了解,您喜欢遍历您的数据集,而不是增加一个计数器counter 特定于数据集条目的某些属性。

所以这听起来像是GroupJoin() linq 语句的工作。这样,您将获得一个 ItemLimits 对象,其中所有匹配的数据集项目为IEnumerable&lt;DataSetItem&gt;。然后你可以在这个内部枚举上调用Aggregate(),并将这个信息写入给定的ItemLimits对象。

【讨论】:

    【解决方案3】:

    我已经完全放弃了 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 秒。

    【讨论】:

    • 如果您首先进行转换调用并将值存储在局部变量中,您可以避免一些方法调用,我认为提高可读性
    • @Zache 不错。完成。
    猜你喜欢
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多