假设你需要有一个整数列表/数组,你需要频繁迭代,我的意思是非常频繁。原因可能各不相同,但可以说这是在一个大容量处理的最内部循环的核心。

通常情况下,人们会选择使用列表(List),因为它们在大小上具有灵活性。最重要的是,msdn文档声称列表在内部使用数组,并且应该执行得一样快(快速查看Reflector证实了这一点)。尽管如此,还是有一些开销。

有人测量过吗?在一个列表中迭代6M次是否与数组相同?


当前回答

不要试图通过增加元素数量来增加容量。

性能

List For Add: 1ms
Array For Add: 2397ms

    Stopwatch watch;
        #region --> List For Add <--

        List<int> intList = new List<int>();
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 60000; rpt++)
        {
            intList.Add(rand.Next());
        }
        watch.Stop();
        Console.WriteLine("List For Add: {0}ms", watch.ElapsedMilliseconds);
        #endregion

        #region --> Array For Add <--

        int[] intArray = new int[0];
        watch = Stopwatch.StartNew();
        int sira = 0;
        for (int rpt = 0; rpt < 60000; rpt++)
        {
            sira += 1;
            Array.Resize(ref intArray, intArray.Length + 1);
            intArray[rpt] = rand.Next();

        }
        watch.Stop();
        Console.WriteLine("Array For Add: {0}ms", watch.ElapsedMilliseconds);

        #endregion

其他回答

测量结果很好,但是根据您在内部循环中所做的具体操作,您将得到显著不同的结果。衡量你自己的情况。如果您正在使用多线程,那么这本身就不是一个简单的活动。

在一些简短的测试中,我发现两者的结合在我所谓的合理密集数学中会更好:

类型:<双[]>列表

时间:00:00:05.1861300

类型:列表<列表<double>>

时间:00:00:05.7941351

类型:double[行*列]

时间:00:00:06.0547118

运行代码:

int rows = 10000;
int columns = 10000;

IMatrix Matrix = new IMatrix(rows, columns);

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();


for (int r = 0; r < Matrix.Rows; r++)
    for (int c = 0; c < Matrix.Columns; c++)
        Matrix[r, c] = Math.E;

for (int r = 0; r < Matrix.Rows; r++)
    for (int c = 0; c < Matrix.Columns; c++)
        Matrix[r, c] *= -Math.Log(Math.E);


stopwatch.Stop();
TimeSpan ts = stopwatch.Elapsed;

Console.WriteLine(ts.ToString());

我真希望我们有一些顶尖的硬件加速矩阵类,就像。net团队用system . numbers . vectors类做的那样!

c#可能是最好的ML语言,只要在这方面多做一些工作!

由于List<>在内部使用数组,因此基本性能应该是相同的。为什么这个列表可能会稍微慢一些,有两个原因:

要在列表中查找元素,调用list方法,该方法在底层数组中进行查找。所以你需要一个额外的方法调用。另一方面,编译器可能会识别出这一点,并优化“不必要的”调用。 如果编译器知道数组的大小,它可能会做一些特殊的优化,而对于一个未知长度的列表,它就不能这样做。如果列表中只有几个元素,这可能会带来一些性能改进。

要检查它是否对您有任何影响,最好将发布的计时函数调整为您计划使用的大小列表,并查看您的特殊情况的结果如何。

如果你只是从其中一个中获得一个值(不是在循环中),那么两者都进行边界检查(记住,你在托管代码中),只是列表做了两次。 请参阅后面的注释,了解为什么这可能不是什么大问题。

如果你正在使用你自己的for(int int i = 0;i < x.[Length/Count];i++)则键差如下所示:

数组: 边界检查被移除 列表 执行边界检查

如果你使用foreach,关键区别如下:

数组: 没有分配对象来管理迭代 边界检查被移除 List通过一个已知为List的变量。 迭代管理变量是堆栈分配的 执行边界检查 列表通过一个已知为IList的变量。 迭代管理变量是堆分配的 执行边界检查 also Lists的值在foreach过程中不能改变,而数组的值可以改变。

边界检查通常不是什么大问题(特别是如果您在一个具有深层管道和分支预测的cpu上——这是目前大多数情况下的常态),但只有您自己的分析才能告诉您这是否是一个问题。 如果你在代码中避免堆分配(很好的例子是库或hashcode实现),那么确保变量类型为List而不是IList将避免这个陷阱。 和往常一样,如果重要的话。

我想表演会很相似。 在使用List和Array时所涉及的开销是,恕我直言,当您向列表中添加项时,当列表必须增加它在内部使用的数组的大小时,当数组的容量达到时。

假设你有一个容量为10的List,那么一旦你想添加第11个元素,List就会增加它的容量。 可以通过将列表的Capacity初始化为它将容纳的项数来减少性能影响。

但是,为了弄清楚遍历List是否与遍历数组一样快,为什么不对其进行基准测试呢?

int numberOfElements = 6000000;

List<int> theList = new List<int> (numberOfElements);
int[] theArray = new int[numberOfElements];

for( int i = 0; i < numberOfElements; i++ )
{
    theList.Add (i);
    theArray[i] = i;
}

Stopwatch chrono = new Stopwatch ();

chrono.Start ();

int j;

 for( int i = 0; i < numberOfElements; i++ )
 {
     j = theList[i];
 }

 chrono.Stop ();
 Console.WriteLine (String.Format("iterating the List took {0} msec", chrono.ElapsedMilliseconds));

 chrono.Reset();

 chrono.Start();

 for( int i = 0; i < numberOfElements; i++ )
 {
     j = theArray[i];
 }

 chrono.Stop ();
 Console.WriteLine (String.Format("iterating the array took {0} msec", chrono.ElapsedMilliseconds));

 Console.ReadLine();

在我的系统上;遍历数组需要33msec;遍历列表花费了66msec。

说实话,我没想到变化会这么大。 所以,我把我的迭代放在一个循环中:现在,我执行了1000次迭代。 结果如下:

迭代List需要67146毫秒 迭代数组需要40821毫秒

现在,变化不再那么大了,但仍然……

因此,我已经启动了。net Reflector, List类的索引器的getter看起来像这样:

public T get_Item(int index)
{
    if (index >= this._size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    return this._items[index];
}

如您所见,当您使用List的索引器时,List会执行一次检查,检查您是否没有超出内部数组的边界。这种额外的检查是有成本的。