【问题标题】:Slow performance from ImmutableList<T> Remove method in Microsoft.Bcl.ImmutableMicrosoft.Bcl.Immutable 中 ImmutableList<T> Remove 方法的性能下降
【发布时间】:2014-09-07 05:41:39
【问题描述】:

从 NuGet 包 Microsoft.Bcl.Immutable 版本 1.0.34 和 1.1.22-beta 中体验 Microsoft ImmutableList 的一些意外性能

从不可变列表中删除项目时,性能非常缓慢。 对于包含 20000 个整数值 (1...20000) 的 ImmutableList,如果开始从值 20000 删除到 1,则从列表中删除所有项目大约需要 52 秒。 如果我对通用 List&lt;T&gt; 执行相同操作,我会在每次删除操作后创建列表的副本,大约需要 500 毫秒。

我对这些结果有点惊讶,因为我认为 ImmutableList 会比复制通用 List&lt;T&gt; 更快,但也许这是意料之中的?

示例代码

// Generic List Test
var genericList = new List<int>();

var sw = Stopwatch.StartNew();
for (int i = 0; i < 20000; i++)
{
    genericList.Add(i);
    genericList = new List<int>(genericList);
}
sw.Stop();
Console.WriteLine("Add duration for List<T>: " + sw.ElapsedMilliseconds);
IList<int> completeList = new List<int>(genericList);

sw.Restart();

// Remove from 20000 -> 0.
for (int i = completeList.Count - 1; i >= 0; i--)
{
    genericList.Remove(completeList[i]);
    genericList = new List<int>(genericList);
}
sw.Stop();
Console.WriteLine("Remove duration for List<T>: " + sw.ElapsedMilliseconds);
Console.WriteLine("Items after remove for List<T>: " + genericList.Count);


// ImmutableList Test
var immutableList = ImmutableList<int>.Empty;

sw.Restart();
for (int i = 0; i < 20000; i++)
{
    immutableList = immutableList.Add(i);
}
sw.Stop();
Console.WriteLine("Add duration for ImmutableList<T>: " + sw.ElapsedMilliseconds);

sw.Restart();

// Remove from 20000 -> 0.
for (int i = completeList.Count - 1; i >= 0; i--)
{
    immutableList = immutableList.Remove(completeList[i]);
}
sw.Stop();
Console.WriteLine("Remove duration for ImmutableList<T>: " + sw.ElapsedMilliseconds);
Console.WriteLine("Items after remove for ImmutableList<T>: " + immutableList.Count);

更新

如果从 ImmutableList 的开头删除项目,就像使用普通的 foreach 循环一样,那么性能会好很多。删除所有项目只需不到 100 毫秒。 这不是您在所有情况下都可以做的事情,但很高兴知道。

【问题讨论】:

  • 您是否尝试过使用RemoveAt 方法?见msdn.microsoft.com/en-us/library/dn456151.aspx
  • 我现在删除了 2 个答案,因为我误解了上面的代码和 ImmutableList 的实现。 @Servy 对 ImmutableList 的性质有一些有用的 cmets - “It's implemented as a binary search tree”,这些在我删除的帖子中丢失了。
  • 没有尝试过RemoveAt@KrisVandermotten,因为它在我的现实世界场景中没有用。可能会坚持使用旧的 List&lt;T&gt; 并结合使用锁定和快照,而不是使用 ImmutableList&lt;T&gt;
  • 如果RemoveAt 在您的现实世界场景中没有用,那么此测试也不代表您的现实世界场景。您正在删除列表末尾的内容,这是一种特殊情况,恰好在可变列表中很快。

标签: c# performance immutability immutablelist


【解决方案1】:

Remove 方法必须扫描整个列表才能找到要删除的元素。删除本身是 O(1),因为只需要弹出最后一个元素。两种算法都具有二次性能。

为什么运行时间有巨大差异?可能是因为ImmutableList 在内部是一个树结构。这意味着要扫描列表,需要大量的指针取消引用和不可预测的分支和内存访问。这很慢。

猜你喜欢
  • 2014-09-06
  • 2010-11-06
  • 1970-01-01
  • 1970-01-01
  • 2014-05-19
  • 2013-12-24
  • 2011-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多