Pull to refresh

Comments 8

Выяснилось, что с уборкой мусора время работы значительно меньше, чем без неё! Хотя, казалось бы, на работу сборщика мусора должно тратиться время, что должно было ухудшить результат.

Попробуйте пул объектов добавить.

Я вижу кучу созданий
Dictionary<TreeNodeEnd, HashSet<TreeNodeEnd>> SelectedTerms =
      new Dictionary<TreeNodeEnd, HashSet<TreeNodeEnd>>();
new HashSet<TreeNodeEnd>();
new LinkedList<TreeNodeEnd>();

Попробуйте их переиспользовать. Ну и вообще попрофилируйте конструкторы.

Плюс, не понял проблемы со ссылками на структуры. Ключевое слово ref как раз для ссылок на значимые типы используется.
По поводу пула объектов не получится, т. к. нехватка памяти происходит в одном месте одной процедуры, где массово создаются объекты типа TreeNodeEnd. “Вылечить” я это пытался путём полной сборки мусора перед вызовом этой процедуры примерно так:
…
GC.Collect();
Skleivanie(…);
…
  private void Skleivanie(…)
  {
…
    foreach (…)
    {
      …
      new TreeNodeEnd(…); //Здесь происходит ошибка выделения памяти
      …
    }
…
  }

Но при количестве входных переменных больше 15 происходит столько выделений TreeNodeEnd, что памяти не хватает.
Окей, раз GC.COllect() помогает — есть два варианта. Первый — он дефрагментирует память, и туда влезает больше TreeNodeEnd. Тут уже ничего поделаешь, ограничение памяти.
Другой вариант — сборщик мусора таки собирает какие-то объекты на каждой итерации. Вот тут
while (X1Tree.Count != 0)
      {
        Dictionary<UInt64, TreeNodeEnd> X2Tree = new Dictionary<UInt64, TreeNodeEnd>();
        Skleivanie(X1Tree, X2Tree, OutTemp, iLevelCounter++);
        X1Tree = X2Tree;
        GC.Collect(); //Отсутствие сборки мусора очень сильно влияет на время работы!!!
      }

видно что Skleivanie() вызывается в цикле, там же сборщик отрабатывает.
Если сборщик собирает объекты — их можно просто переиспользовать, что в принципе может дать выигрыш в производительности. Отпрофилируйте программу до сборщика и после, увидите что именно он собирает.

Если не хотите\не умеете профилировать — добавьте счетчики для вызываемых конструкторов.
static class Factory<T>
{
  public static T GetNew<T>()
  {
    Counter<TreeNodeEnd>.Increase(); 
    return new TreeNodeEnd(…); 
  }
}


увидите какие объекты создаются чаще всего и забирают больше всего времени. На реюзе объектов сможете выиграть если не в памяти, то в скорости.

И да, GC.Collect() обычно вызывают два раза подряд.
andy_p
А сравните вашу программу с этим методом:

https://habrahabr.ru/post/283030

Прошёл по ссылке, которая есть в статье на сайт, где выложен архив программы, но не понял как ею пользоваться и исходников там тоже нет.
UFO just landed and posted this here
babylon,
Я не сторонник использования дополнительных библиотек без существенной на это необходимости. Зачем привлекать лишние сущности, если можно писать на чистом C#? В своей реализации использовал Generic-коллекции .NET только потому, что с ними быстрее работает и код короче, т. к. не нужно реализовывать свои контейнеры.
Окей, раз GC.COllect() помогает — есть два варианта. Первый — он дефрагментирует память, и туда влезает больше TreeNodeEnd. Тут уже ничего поделаешь, ограничение памяти.

Oxoron, в данном случае похоже имеет место именно этот случай. При 16 переменных на одной из итераций выделяется порядка десятка миллионов объектов TreeNodeEnd, что банально не хватает памяти.
Если бы TreeNodeEnd была структурой, то это несколько бы съэкономило память. Но т. к. в коде используются указатели на TreeNodeEnd, которые в том числе могут иметь значение NULL, то этот вариант реализовать не получается.
Sign up to leave a comment.

Articles