2010-10-20 38 views
3

我正在研究一些需要非常低的延迟并推送大量内存的应用程序,并正在对一些应用程序进行测试。分配一个列表ad-hoc与预分配和清除列表执行。 我期待测试运行时预分配内存的执行速度要快得多,但令我惊讶的是它们实际上稍慢(当我让测试运行10分钟时,平均差异大约为400ms)。.NET预分配内存vs临时分配

下面是测试代码,我用:

class Program 
{ 
    private static byte[] buffer = new byte[50]; 
    private static List<byte[]> preAlloctedList = new List<byte[]>(500); 

    static void Main(string[] args) 
    { 
     for (int k = 0; k < 5; k++) 
     { 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 

      for (int i = 0; i < 1000000; i++) 
      { 
       List<byte[]> list = new List<byte[]>(300); 

       for (int j = 0; j < 300; j++) 
       { 
        list.Add(buffer); 
       } 
      } 

      sw.Stop(); 
      Console.WriteLine("#1: " + sw.Elapsed); 
      sw.Reset(); 
      sw.Start(); 

      for (int i = 0; i < 1000000; i++) 
      { 
       for (int j = 0; j < 300; j++) 
       { 
        preAlloctedList.Add(buffer); 
       } 

       preAlloctedList.Clear(); 
      } 
      sw.Stop(); 
      Console.WriteLine("#2: " + sw.Elapsed); 
     } 

     Console.ReadLine(); 
    } 
} 

现在,什么是真正有趣的,我跑并排性能监视器侧,看到下面的图案看起来像我的预期:

绿色=第0级集合
蓝色=分配的字节/秒
红色=%在GC时间

下面的控制台应用程序显示#1和试验运行时#2
alt text

所以,我的问题是,为什么测试#1比#2更快?
显然,我宁愿在我的应用程序中测试#2的perfmon统计数据,因为基本上没有内存压力,没有GC集合等,但#1似乎稍快一点?
List.Clear()会带来多大的开销吗?

感谢,

汤姆

编辑 我做了另外一个测试,用相同的设置,但运行与服务器GC的应用启用,目前#2变稍快 alt text

回答

4

我怀疑测试#1更快的原因是垃圾收集发生在单独的线程上,并且分配的开销低于额外的List<T>.Clear调用。由于这些列表都不是很大(每个只有300个引用),并且它们都是在紧密循环中创建和取消的,所以它们通常都会保留在Gen 0中。

我在分析过程中注意到了这一点过去 - 重复使用List<T>并对其调用Clear通常比重新分配要慢。 Clear()实际上会清除内部数组以及重置列表的参数,我认为这些参数比列表的初始分配开销(略)多。

但是,在我看来,这个例子实际上只是表明.NET中的GC非常非常高效。

+0

非常感谢您的回复。你会选择哪一个选项,但在非常相似的生产场景中?现在,有趣的是,当我启用服务器GC运行相同的应用程序时,#2实际上变得更快。我更新了这篇文章,并附上截图 – TJF 2010-10-20 19:37:29

+0

@Tom:服务器模式以更高的延迟为代价提高了整体吞吐量。哪个更好取决于你。话虽如此,我通常只在我的生产环境中使用#1 - 代码更干净,而且它的整体性能更好。 – 2010-10-20 20:01:21

3

List.Clear()会带来那么多开销吗?

是的,与(单个)GC.Collect(0)相比,对Clear()进行几千次调用可能会更慢。

据我所知,dotNet内存系统在分配/释放短暂内存块方面速度非常快。

但要小心从这个简单的测试结果到您的真实应用程序。

+0

我看了清单的实现和Clear()做了一个extern调用。你碰巧知道了什么成就?我可以使用Clear()只实现一个List的实现,只重置大小字段 – TJF 2010-10-20 19:48:40

+0

从不知道,这是Clear所做的:根据元素类型,将数组中元素的范围设置为零,为假或为空。 – TJF 2010-10-20 19:50:37

+0

@Tom:您必须清除数组,否则您将保留所有元素的根,因此GC无法收集它们。一般来说,我会尽量避免像这样重新发明轮子...... – 2010-10-20 20:02:16