【问题标题】:List<> looping optimizationList<> 循环优化
【发布时间】:2017-02-18 21:50:35
【问题描述】:

我有一个项目可以处理大量相同类型的对象。

现在我使用List&lt;Person&gt;,但是当我有大约 1 000 000 个项目时,循环遍历这个列表似乎很困难。

循环中,每个Person都有一个方法被调用,随机生成新的item,也有一些item被删除。

我可以做些什么来优化这个循环? 我应该更改集合类型,还是将项目移至数据库?

这是循环的样子:

while (_worldIsLiving)
{
    int beginningPopulation = WorldPopulationNumber;

    for (int i = 0; i < beginningPopulation; i++)
    {
        _worldPopulation[i].InvokeSkills(this);
    }

    // Remove dead persons
    for (int i = 0; i < WorldPopulationNumber;)
    {
        if (_worldPopulation[i].IsDead()) _worldPopulation.RemoveAt(i);
        else i++;
    }

    WorldCycles++;
    if (_hasStopCondition && (WorldPopulationNumber >= WorldMaximumPopulation || WorldPopulationNumber == 0))
        Destroy();
}

_worldPopulation[i].InvokeSkills(this);可以生成新的人。

技能chanceToBeInvokenchanceTobeInherited字段。

【问题讨论】:

  • 唯一更快迭代的就是普通数组,但这可能只会产生微小的差异。任何优化通常都在循环中的代码中,而不是容器中。要回答这个问题,我们需要更多地了解循环中的代码。
  • 当您说“循环遍历此列表很困难”时,您所说的“困难”是什么意思?这就像循环一个只有几个元素的列表一样简单——只是需要更长的时间。
  • 项目存储在内存中。 Person有技能列表,每个技能都有机会被调用。主循环中的代码:我调用Person 类中调用随机技能的方法。 @MatthewWatson 循环很难,因为在大量项目上,一个循环大约需要一分钟。 @Enigmativity
  • 似乎很可能关注循环而不是循环中执行的操作是浪费时间。因此,您的问题完全缺乏正确回答的信息。
  • @MarkBenovsky _worldPopulation.RemoveAt(i) 在列表上将是一项昂贵的操作。它涉及将每个后续项目向下分流一个位置,并且您将为每个“死”实例执行多次此操作。对于一个长列表,这将增加相当大的开销,具有 O(n2) 的性能(如果我的 CS 帽子今天戴得正确的话)。只写一个新列表可能会快得多:_worldPopulation = _worldPopulation.Where(p=&gt;!p.IsDead())

标签: c# optimization collections parallel-processing large-data


【解决方案1】:

_worldPopulation.RemoveAt(i) 在列表上将是一项昂贵的操作。

它涉及将每个后续项目向下分流一个位置。您在外循环的每次迭代中多次调用它,每个“死”实例一次。

对于一个长列表,这将增加非常大的开销,复杂度为 O(n2)(如果我的 CS 帽子今天戴得正确的话)。

只写一个新列表可能会快得多:

_worldPopulation = _worldPopulation.Where(p=>!p.IsDead())

如果这看起来仍然很昂贵,那么修剪列表是否重要? (该列表能否包含所有活着和死去的人口,或者这会占用可用内存吗?)

例如,您可以:

var livingPopulace = _worldPopulation.Where(p => !p.IsDead());
foreach(var pop in livingPopulace)
{
    pop.InvokeSkills(this)
}
var newPopulationCount = _worldPopulation.Count(p => !p.IsDead());

虽然这需要对集合进行 2 次扫描,但与使用 RemoveAt 相比,它仍然会减少通过集合的次数,尤其是在每个周期的死亡率很高的情况下。您又回到了 O(n) 复杂度,我怀疑对于大型集合,这将比使用 RemoveAt 更有效。

如果这些都不令人满意,您可以考虑使用LinkedList&lt;T&gt; 作为您的容器,它支持简单的前向迭代和低成本的移除(以随机访问索引为代价),但可能存在其他限制因素不切实际的。您提供的代码中没有任何内容表明这不起作用。只是不要被ElementAt 之类的 Linq 运算符所诱惑,以绕过随机访问限制,否则您将回到同样的问题。

【讨论】:

  • 谢谢,写出一个新列表而不是 RemoveAt 会快一点,但还不确定 LinkedList。
猜你喜欢
  • 1970-01-01
  • 2017-07-20
  • 1970-01-01
  • 2021-01-21
  • 2013-07-17
相关资源
最近更新 更多