.NET предварительное выделение памяти против специального распределения - PullRequest
3 голосов
/ 20 октября 2010

Я работаю над некоторыми приложениями, которые требуют очень низкой задержки и занимают много памяти, и некоторое время тестировал, как, например, выделение списка 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 бок о бок и увидел следующую схему, которая выглядит так, как я ожидал:

Зеленый = Gen 0 коллекций
Синий = Выделенные байты / сек
Красный =% Время в ГХ

Консольное приложение ниже показывает время выполнения теста для # 1 и # 2
alt text

Итак, мой вопрос: почему Тест № 1 быстрее, чем # 2?
Очевидно, что я предпочел бы иметь в своем приложении статистику Permon Test # 2, так как в основном нет нагрузки на память, нет сборок GC и т. Д., Хотя # 1, кажется, немного быстрее?
Несет ли List.Clear () столько накладных расходов?

Спасибо

Tom

EDIT Я сделал еще один тест, с той же настройкой, но запустив приложение с включенным GC сервера, теперь № 2 становится немного быстрее alt text

Ответы [ 2 ]

4 голосов
/ 20 октября 2010

Я подозреваю, что причина, по которой Тест № 1 выполняется быстрее, заключается в том, что сборка мусора происходит в отдельном потоке, а накладные расходы на выделение меньше, чем при дополнительном вызове List<T>.Clear. Поскольку ни один из этих списков не является большим (всего 300 ссылок в каждом), и все они создаются и удаляются в узком цикле, все они, как правило, остаются в Gen 0.

Я заметил это во время профилирования - повторное использование List<T> и вызов Clear для него часто медленнее, чем просто перераспределение. Clear() на самом деле очищает внутренний массив, а также сбрасывает параметры списка, который, я считаю, имеет (немного) больше накладных расходов, чем первоначальное распределение списка.

Однако этот пример, на мой взгляд, действительно показывает, что сборщик мусора в .NET очень, очень эффективен.

3 голосов
/ 20 октября 2010

Неужели List.Clear () несет столько накладных расходов?

Да, по сравнению с (единственным) GC.Collect(0) выполнение нескольких тысяч вызовов Clear() вполне можетбудь медленнее.

Насколько я понял, система памяти dotNet очень быстро распределяет / освобождает блоки недолговечной памяти.

Но будьте осторожны, перенесите выводы из этого простого теста в ваше реальное приложение.

...