【问题标题】:Performance difference with various List initialization and population techniques各种列表初始化和填充技术的性能差异
【发布时间】:2017-03-23 16:00:51
【问题描述】:

我试图找出在构造函数中设置列表的容量是否可以缩小使用 List<T>(IEnumerable<T>)List<T>() + AddRange(IEnumerable<T>) 之间已经可以忽略不计的性能差异。

我发现在AddRange() 之前设置容量实际上导致使用初始集合构造列表的性能大致相同

但还有更多。如果您使用Add() 而不是AddRange() 会怎样?出于某种原因,性能似乎提高了 32%

如果我使用Add() 而不用初始容量初始化列表的构造函数怎么办?性能似乎比之前的 Add() 方法最差,但仍优于 AddRange() 方法:性能提升了 26%。

这很奇怪,让我怀疑我的测试是否有效;所以我发布了我所做的测试(在没有调试器的发布模式下运行)。

有人可以确认吗?

int items = 100000;
int cycles = 10000;

var collectionToCopy = Enumerable.Range( 0, items );

var sw0 = new Stopwatch();
sw0.Start();
for( int i = 0; i < cycles; i++ )
{
    List<int> list = new List<int>( collectionToCopy );
}
sw0.Stop();
Console.WriteLine( sw0.ElapsedMilliseconds );

var sw1 = new Stopwatch();
sw1.Start();
for( int i = 0; i < cycles; i++ )
{
    List<int> list = new List<int>( items );
    list.AddRange( collectionToCopy );
}
sw1.Stop();
Console.WriteLine( sw1.ElapsedMilliseconds );

var sw4 = new Stopwatch();
sw4.Start();
for( int i = 0; i < cycles; i++ )
{
    List<int> list = new List<int>();
    list.AddRange( collectionToCopy );
}
sw4.Stop();
Console.WriteLine( sw4.ElapsedMilliseconds );

var sw2 = new Stopwatch();
sw2.Start();
for( int i = 0; i < cycles; i++ )
{
    List<int> list = new List<int>( items );
    foreach( var item in collectionToCopy )
        list.Add( item );
}
sw2.Stop();
Console.WriteLine( sw2.ElapsedMilliseconds );

var sw3 = new Stopwatch();
sw3.Start();
for( int i = 0; i < cycles; i++ )
{
    List<int> list = new List<int>();
    foreach( var item in collectionToCopy )
        list.Add( item );
}
sw3.Stop();
Console.WriteLine( sw3.ElapsedMilliseconds );

结果:

13400 - 用集合初始化构造函数

13423 - 构造函数初始化为容量 + AddRange(collection)

14857 - 无参数构造函数 + AddRange(collection)

9003 - 构造函数初始化为容量 + Add(item)

9841 - 无参数构造函数 + Add(item)

【问题讨论】:

  • 仅供参考:我取得了非常相似的结果,与您的没有太大区别。 AddRange() 比 Add() 慢似乎很奇怪也许一些 C# 大师会详细说明。

标签: c# performance list


【解决方案1】:

魔鬼在Enumerable.Range(0, items)。将其更改为 Enumerable.Range(0, items).ToList(); 将为您带来预期的结果。

AddRange(IEnumerable collectionToCopy) 将检查输入的实际类型。如果输入是连续的(例如列表,数组,...),它将尝试复制。否则,它会退回到枚举和简单的 Add()。

这是后备代码

using(IEnumerator<T> en = collection.GetEnumerator()) {
   while(en.MoveNext()) {
        Insert(index++, en.Current);                                    

在备用情况下,List.AddRange 给出的较差性能可能来自 IEnumertor 开销

【讨论】:

    猜你喜欢
    • 2015-07-16
    • 2016-04-02
    • 2011-09-26
    • 1970-01-01
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    • 2013-02-25
    • 2015-02-15
    相关资源
    最近更新 更多