2016-04-03 4 views
1

Я пишу симуляцию, которая имеет цикл обновления, который называется каждым фреймом. В функции обновления у меня есть миллионы объектов для обновления, так что это выглядит так.C# как уменьшить сбор мусора

public void Update(float deltaTime) 
{ 
    Time += deltaTime; 
    foreach (SimulationObject obj in objects.ToArray()) 
     obj.Update(deltaTime); 
} 

where objects is 
List<SimulationObject> objects = new List<SimulationObject>(); 
and is populated at program initialization time. 

Вы можете, вероятно, увидеть objects.ToArray() делает копию этого огромного списка каждый кадр, то копия получает сборщик мусора. Это вызывает огромную производительность для меня. Для пробега около 2 минут собранный мусор достигает около 2G. Поскольку список этих объектов асинхронно модифицируется в фоновом режиме некоторой сторонней библиотекой, кажется, что я не могу удалить ToArray().

Мне интересно, если есть хороший способ уменьшить сбор мусора, избегайте копирования или повторного использования выделенного пространства?

Я новичок в C# и попытался найти ответы, но не смог. Если это дублированный пост, я приношу свои извинения.

+2

FYI .ToArray() делает копию коллекции, хотя объекты в коллекции не копируются http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs, 783a052330e7d48d – Eminem

+0

См. Ответ на: [Предотвратите сборку мусора в течение короткого периода времени] (http://stackoverflow.com/questions/6005865/prevent-net-garbage-collection-for-short-period-of-time) – Eminem

+1

@Eminem - предполагается, что 'SimulationObject' - это' class' (ссылочный тип), а не 'struct' (тип значения). – Corak

ответ

-1

Там нет оснований называть ToArray(), так что вы можете просто использовать

public void Update(float deltaTime) 
{ 
    Time += deltaTime; 
    foreach (SimulationObject obj in objects) 
     obj.Update(deltaTime); 
} 
+2

Это вызовет [InvalidOperationException] (https://msdn.microsoft.com/library/system.invalidoperationexception.aspx) - «коллекция была изменена» - если другой поток изменяет коллекцию при ее переходе через нее, как это было сказано в OP. – Corak

3

ToArray() не действительно необходимо здесь, это просто пустая трата времени и памяти. Используйте петлю for. Я хотел бы начать с чем-то вроде

for (int i = 0; i < objects.Count; i++) 
{ 
    SimulationObject obj = null; 
    try 
    { 
     obj = objects[i]; 
    } 
    catch (ArgumentOutOfRangeException) 
    { 
     // something was removed from the collection 
     // we reached the end of the list 
     break; 
    } 

    obj.Update(deltaTime) 
} 

Этот код расщепляет obj = objects[i] и obj.Update(deltaTime) намеренно для того, чтобы избежать прерывания цикла, если Update() бросает ArgumentOutOfRangeException.

знать о двух фактах:

  • Некоторые объекты будут пропускать первую итерацию после того как они были добавлены к objects коллекции, которая не должна действительно быть проблемой, так как их дополнение является асинхронным конструкцией.
  • Некоторые объекты, вероятно, будут обновляться только один раз после того, как они будут удалены из списка objects (естественно, одновременно с удалением, но результат имеет тенденцию появляться, как будто это происходит «после»). В зависимости от моделирования этот случай может быть нежелательным и, вероятно, потребует определенной обработки.
+1

Это фактически обходит это исключение (хотя может быть и другое исключение). Это, вероятно, делает эту работу. – Corak

+0

Упс мой плохой. Я думаю, было бы лучше, если бы OP использовала потокобезопасные коллекции в первую очередь или поддерживала два списка. – MickyD

+0

Если вы переходите в обратном направлении, вероятность выброса исключения значительно уменьшается. – egrunin

-1

Как кто-то указал, что вам не нужно использовать метод toArray(), поскольку он просто создаст копию вашего текущего списка.

Если я правильно понимаю, вы делаете это, так как ваш список изменяется сторонним lib в фоновом режиме.

Я думаю, что вы можете безопасно использовать цикл Parallel.ForEach в этом случае, так как вы сами не изменяете список, только элементы в списке.

 Float Time; // some value 
     Float deltaTime; // some value 
     Parallel.ForEach(objects, currObject => 
      { 
       // do stuff 
      Interlocked.increment(ref Time,deltaTime); 
      currObject.Update(Time) 
      }