.NET预分配内存与ad-hoc分配

时间:2010-10-20 19:11:43

标签: .net memory garbage-collection

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

以下是我使用的测试代码:

    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();
    }
}

现在,真正有趣的是,我正在并排运行perfmon并看到了以下模式,看起来像我预期的那样:

绿色=第0代收藏品 蓝色=分配的字节数/秒
红色= GC中的%时间

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

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

谢谢,

汤姆

修改 我做了另一个测试,使用相同的设置,但运行启用了服务器GC的应用程序,现在#2变得稍快 alt text

2 个答案:

答案 0 :(得分:4)

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

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

但是,在我看来,这个例子确实表明.NET中的GC非常非常有效。

答案 1 :(得分:3)

  

List.Clear()是否带来了那么多开销?

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

据我所知,dotNet内存系统在分配/解除分配短期内存块方面非常快。

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