【问题标题】:Why does a simple List<T> seem to be slower than an ArrayList?为什么简单的 List<T> 似乎比 ArrayList 慢?
【发布时间】:2012-03-22 17:57:23
【问题描述】:

出于好奇,我想测试滴答数以将 GenericList 与 ArrayList 进行比较。

对于下面的代码,当我检查秒表时,ArrayList 似乎更快。

我做错了什么还是对此有解释? (我相信 List 会快得多)

测试代码和下面的输出

private static void ArrayListVsGenericList()
{
    // Measure for ArrayList
    Stopwatch w0 = new Stopwatch();
    w0.Start();

    ArrayList aList = new ArrayList();

    for (int i = 0; i < 1001; i++)
    {
        Point p = new Point();
        p.X = p.Y = i;

        aList.Add(p);
    }

    foreach (Point point in aList)
    {
        int v0 = ((Point) aList[8]).X; //unboxing
    }


    w0.Stop();

    // Measure for Generic List<Point>
    Stopwatch w1 = new Stopwatch();
    w1.Start();

    List<Point> list = new List<Point>();

    for (int i = 0; i < 1001; i++)
    {
        Point p = new Point();
        p.X = p.Y = i;

        list.Add(p); 
    }


    foreach (var point in list)
    {
        int v1 = list[8].X;
    }

    w1.Stop();

    Console.WriteLine("Watch 0 : " + w0.ElapsedTicks);
    Console.WriteLine("Watch 1 : " + w1.ElapsedTicks);
    Console.WriteLine("Watch 0 > Watch 1 : " + (w0.ElapsedTicks > w1.ElapsedTicks));
}

【问题讨论】:

  • 我认为这个测试不可靠。首先,代码执行的顺序很重要。添加第二个列表可能会受到先前分配的影响。 1000 个元素也是一个小集合。您绝对应该在两个不同的程序中将两种列表的填充和获取分开,然后使用分析器而不是秒表来测试它们。
  • 使用ints 代替Points 的效果如何?什么时候先测试通用列表?
  • 好吧,当我将元素数量增加到 1000001 时,泛型会更快。但这是否意味着泛型在处理少量元素时不能被认为是最快的?
  • 您应该在它们的构造函数中使用初始容量初始化这两个列表,以减少for 循环期间的分配成本。出于同样的原因,比较 foreach 循环的性能如果您多次而不是只运行一次,也会很有趣。
  • @perncilCake:您可以通过以下方式获得更多信息:stackoverflow.com/questions/5251335/…

标签: c# .net generics arraylist clr


【解决方案1】:

更改您的测试程序以至少运行您的方法两次并忽略第一次运行。结果是由具体类型List&lt;Point&gt; 的代码生成和抖动引起的。

在我的机器上,这会导致以下输出:

  Watch 0 : 154
  Watch 1 : 74
  Watch 0 > Watch 1 : True

这几乎是人们所期望的。

【讨论】:

  • 你是对的!在第二次运行时,即使对于一小部分元素,它也快得多。 (所以在这种情况下,如果我运行 NGEN.exe;我会确保第一次运行对于 Generic List 来说也很快)
【解决方案2】:

您没有消除 JIT 等首次执行效果。泛型需要为每个值类型参数编译一次。

ArrayList 已经用 ngen 进行了预编译。

List&lt;T&gt; 仅针对某些参数类型进行了预编译(我读到核心库为常见参数实例化了一些最重要的泛型,例如 object、bool、int 等),如果有的话。所以它会产生一次性成本。

您还应该注意,ArrayList 的大部分性能成本是间接的:装箱对 GC 施加了更高的压力,并且使用了更多的内存。但是您的测试无法衡量这一点。产生更多垃圾的成本还取决于存在多少其他对象,以及对象的生命周期。

当您编写测试时,您应该在实际测试之前执行所有代码一次,或者使用如此多的迭代以使一次成本可以忽略不计。使用发布版本并运行不附加调试器也很重要。

【讨论】:

    猜你喜欢
    • 2017-01-18
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    相关资源
    最近更新 更多