2010-10-20 2 views
3

Я работаю над некоторыми приложениями, которые требуют очень низкой задержки и многократно загружают память и проводят некоторые проверки того, как, например, выделение списка ad-hoc против предварительного распределения и очистки списка. Я ожидал, что тестовые прогоны предопределяют память для выполнения намного быстрее, но, к моему удивлению, они на самом деле немного медленнее (когда я пропустил тест на 10 минут, средняя средняя - около 400 мс)..NET pre-allocating memory vs ad-hoc allocation

Вот тестовый код, который я использовал:

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

Теперь, что действительно интересно, я бегу системного монитора бок о бок и увидел следующую картину, которая выглядит, как я ожидал:

Green = Gen 0 коллекции
Синий = Выделено байт/сек
Red =% времени в GC

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

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

Спасибо,

Том

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

ответ

4

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

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

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

+0

Спасибо, за ваш ответ это имеет смысл. Какой вариант вы бы выбрали, хотя в сценарии производства, который очень похож? Теперь, что очень интересно, когда я запускаю одно и то же приложение с включенным GC сервера, # 2 на самом деле становится быстрее. Я обновил сообщение и приложил скриншот – TJF

+0

@Tom: Режим сервера ускоряет общую пропускную способность за счет более высокой задержки. Что лучше, зависит от вас. При этом я обычно использую # 1 в своей производственной среде - код чище, и он, как правило, лучше работает в целом. –

3

Имеет ли List.Clear() перенос такого количества накладных расходов?

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

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

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

+0

Я просмотрел список и Clear() делает внешний вызов. Вы случайно не знаете, чего это добивается? Я мог бы сделать реализацию List с помощью Clear() только для сброса поля размера – TJF

+0

nevermind, это то, что делает Clear: Устанавливает диапазон элементов в массиве до нуля, до значения false или null, в зависимости от типа элемента. – TJF

+0

@Tom: вам нужно очистить массив, иначе вы оставите все элементы в корне, поэтому GC не сможет их собрать. В общем, я постараюсь не изобретать колесо таким образом ... –