在列表中添加新项目时出现奇怪的速度差异(C#)

在列表中添加新项目时出现奇怪的速度差异(C#),c#,performance,list,C#,Performance,List,我做了一些关于C#中列表的速度测试。这是一个我无法解释的结果。我希望有人能弄清楚发生了什么事 如果在cloneList.Add(下一步):x毫秒之前调用cloneList.RemoveAt(cloneList.Count-1),则1000次迭代的毫秒数 如果在cloneList.Add(下一步)之前未调用cloneList.RemoveAt(cloneList.Count-1),则1000次迭代的毫秒数:至少20x毫秒 如果一条语句多了一条,那么我的代码速度似乎要快20倍(请参见下面的代码):

我做了一些关于C#中列表的速度测试。这是一个我无法解释的结果。我希望有人能弄清楚发生了什么事

如果在cloneList.Add(下一步):x毫秒之前调用cloneList.RemoveAt(cloneList.Count-1),则1000次迭代的毫秒数

如果在cloneList.Add(下一步)之前未调用cloneList.RemoveAt(cloneList.Count-1),则1000次迭代的毫秒数:至少20x毫秒

如果一条语句多了一条,那么我的代码速度似乎要快20倍(请参见下面的代码):

秒表秒表=新秒表();
随机数=新随机数(100);
TimeSpan caseOneTimeSpan=新的TimeSpan();
TimeSpan caseTowTimeSpan=新的TimeSpan();
int len=1000;
List myList=新列表();
myList.Capacity=len+1;
//填写清单
对于(int i=0;i
将项目添加到列表时,有两种可能:

  • 内部缓冲区足够大,可以添加另一项。项目将放置在下一个可用位置速度:O(1)(这是最常见的情况。)
  • 内部缓冲区不够大。创建一个新的、更大的缓冲区。将所有项目从旧缓冲区复制到新缓冲区。将下一项添加到新缓冲区速度:O(n)(这种情况不应该经常发生)
  • 虽然大多数
    Add
    调用将是O(1),但有些调用是O(n)

    删除最后一项始终为O(1)

    由于
    Add
    有时取决于列表的大小,因此当列表更大时,需要更长的时间(如果任何调用需要新的缓冲区)。如果在添加新项目时总是删除项目,则可以确保内部缓冲区始终有足够的空间


    您可以查看
    List
    Capacity
    属性,查看内部缓冲区的当前大小,并将其与
    Count
    进行比较,后者是列表实际拥有的项数。(因此,
    Capacity Count
    是缓冲区中可用项的数量。)虽然在实际程序中并不经常有用,但在调试或开发应用程序时查看这些工具有助于帮助您了解下面的情况。

    这应该会消除差异:

            // reset stopwatch and clone list
            stopWatch.Reset();
            cloneList = myList.ToList();
            cloneList.Capacity = cloneList.Capacity + 1;   // add this
    
            // case 2 - add without removing
            stopWatch.Start();
            cloneList.Add(next);
            caseTwoTimeSpan += stopWatch.Elapsed;
    

    我认为这与名单的分配方式有关。最初,它们会得到一定数量的内存分配,一旦超过这个分配,就需要时间重新分配。希望其他人能比我更详细地解释这一点…列表的容量可能与caes中的长度相同,因此当您在删除一个项目后添加一个项目时,它会有额外的空间,但如果您不进行修剪,它必须分配更多的内存。你的两个案例做完全不同的事情。+1回答得好。添加到列表中:您可以在调试列表时查看列表的计数与容量,并查看差异。谢谢您的回答。我现在明白了。尽管它似乎工作得更快,但事实并非如此。您可以看到,如果将stopWatch.Start()移动到cloneList.Capacity=。。。线路。但它清楚地表明,在填充列表之前将列表的容量分配到正确的数量是值得的。当您可以更改诊断方法使其看起来比实际更快时,为什么还要麻烦加快代码的速度呢?这与加速无关(或“真的”加速)。这是关于一些我不理解的特定行为。谢谢你的回答。
            // reset stopwatch and clone list
            stopWatch.Reset();
            cloneList = myList.ToList();
            cloneList.Capacity = cloneList.Capacity + 1;   // add this
    
            // case 2 - add without removing
            stopWatch.Start();
            cloneList.Add(next);
            caseTwoTimeSpan += stopWatch.Elapsed;