Автор Тема: Паузы в работе программы вызываемые GC  (Прочитано 32274 раз)

Romiras

  • Sr. Member
  • ****
  • Сообщений: 264
    • Просмотр профиля
    • Romiras Dev Lab

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #31 : Февраль 22, 2012, 01:34:46 pm »
А в .net только один тип сборщика мусора доступен? Просто когда у меня возникла подобная проблема в java, я просто включил инкрементальный сборщик мусора. В среднем нагрузка на процессор чуть повысилась, но паузы исчезли. Плюс в java есть еще и параллельный сборщик мусора (по крайней мере в java7).
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #32 : Февраль 22, 2012, 02:12:07 pm »
А как переключать сборщик мусора?

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #33 : Февраль 22, 2012, 02:34:41 pm »
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Паузы в работе программы вызываемые GC
« Ответ #34 : Февраль 22, 2012, 02:52:12 pm »
Борьюсь с паузами уменьшая количество объектов видимых сборщику мусора.

Прячу объекты на массивах структур.

Миллионы строк загруженных из БД спрятал на больших массивах char[сегмент][смещение]. Вместо System.String для них использую 4-х байтовую структурку в которой закодировано сегмент-смещение начала "строки". Там первая буква -- длина строки, затем собственно содержимое. Когда кончается текущий сегмент создаю новый. Эти строки "вечные" -- их нельзя удалить. Поэтому они пригодны только для "вечных" данных -- как раз для содержимого БД.

Наблюдаемый прогресс в производительности программы радует  :) :) :)

С полиморфными объектами (с виртуальными процедурами) пока не придумал что делать. На массивах структур их спрятать от сборщика мусора не получится, так как для вызова виртуальной процедуры нужен настоящий сэйфный указатель.

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #35 : Февраль 24, 2012, 04:33:30 am »
Можно перейти к своим указателям на полиморфные объекты след. вида:

TYPE
  Object = RECORD
    vt: ANYPTR;
    data: DataDesc
   END;

Имея obj: Object, можем вызвать какой-нибудь метод так:

obj.vt(приведение_к_абстрактному_типу-интерфейсу).vt.DoSomething(data, ....)
Т.е. динамических объектов будет существовать ровно по одному на каждый используемый в программе тип. Они будут просто "представителями" виртуальных таблиц.

Каждый тип имеет массив для хранения данных своих объектов. И data - это может быть, например, такой же сегмент-смещение, как и для Ваших строк.

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Паузы в работе программы вызываемые GC
« Ответ #36 : Февраль 24, 2012, 12:18:10 pm »
Можно перейти к своим указателям на полиморфные объекты след. вида:

TYPE
  Object = RECORD
    vt: ANYPTR;
    data: DataDesc
   END;
Только в vt надо бы не указатель, а тоже индекс в массиве виртуальных таблиц. Предполагается, что объектов Object миллионы и надо минимизировать количество указателей. Для сборщика мусора важно как количество объектов так и количество указателей. Даже если все N-миллионов указателей будут указывать на небольшое количество объектов, то всё равно сборщик мусора должен будет обойти все N-миллионов указателей. И на N-миллионных массивах структур в каждой структуре тоже не должно быть ни одного указателя, а то сборщик мусора будет на них лазить.

Хотя, честно говоря вот к такому:
obj.vt(приведение_к_абстрактному_типу-интерфейсу).vt.DoSomething(data, ....)
я поко не очень готов, а мои коллеги к такому готовы ещё меньше. Скорее всего полифорфные объекты прятать от сборщика мусора мы вообще не будем, пока хватит улучшений от упрятывания простых объектов.

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #37 : Март 01, 2012, 12:18:55 pm »
Кстати, можно ли смоделировать эту проблему/задачу в относительно маленьком куске кода для последующих экспериментов/бенчмарков? C одной стороны такой код должен быть достаточно большим и организован так, чтобы например cстатический и jit анализатор кода в той же жабе не выкинул бы этот код целиком (просто потому что он ничего полезного не делает - побочных эфектов нет, значит код не нужен, циклы можно не крутить). C другой стороны, какой-нибудь IO и прочие бантики (которые приближают эту модель к реальной моделируемой задаче) не должны вносить значимый вклад в получаемую картину.

Просто хочется посмотреть как подобное выглядит на той же жабе (с разными сборщиками мусора), Go и D. И какие пути решения проблемы существуют там.
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Паузы в работе программы вызываемые GC
« Ответ #38 : Март 01, 2012, 12:23:15 pm »
Миллионы строк загруженных из БД спрятал на больших массивах char[сегмент][смещение]. Вместо System.String для них использую 4-х байтовую структурку в которой закодировано сегмент-смещение начала "строки". Там первая буква -- длина строки, затем собственно содержимое. Когда кончается текущий сегмент создаю новый. Эти строки "вечные" -- их нельзя удалить. Поэтому они пригодны только для "вечных" данных -- как раз для содержимого БД.

Позавчера написал структуру UnmanagedString для ручной работы со строками. Её размер 4 байта -- "индекс" в массиве. Буквы лежат в большом массиве char[сегмент][смещение]. Новые сегменты добавляю по мере надобности. Размер одного сегмента выбрал в 4 мегабуквы, то есть в 8 мегабайтов. Масимальная длина строки = 2^16 - 1 букв. Аллокатор памяти самый простой -- на основе пулов блоков памяти.  Свободные блоки памяти никогда не сливаются друг с другом и обратно в операционную систему не возвращаются. Это самый быстрый менеджер памяти: время создания/удаления объекта O(1).  Выравнивание блоков памяти выбрал в 4 буквы (8 байтов). Односвязные списки (стэки) свободных блоков организуются как в менеджере памяти Delphi с помощью размещения "индексов" в самих же свободных блоках (ведь они же свободны!!!) так что память расходуется максимально экономно.

Вчера в одной подсистеме программы заменил string на UnmanagedString -- эффект впечатлил. Если раньше график расхода памяти в зависимости от времени выглядел как бешенная пила, то теперь стала ровная горизонтальная линия -- мусора не генерируется.

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #39 : Март 01, 2012, 12:26:00 pm »
Вчера в одной подсистеме программы заменил string на UnmanagedString -- эффект впечатлил. Если раньше график расхода памяти в зависимости от времени выглядел как бешенная пила, то теперь стала ровная горизонтальная линия -- мусора не генерируется.
Да, я вчера тоже впечатлился (я видел тот график, точнее, сколь я помню, там было всего 6 графиков - по три графика на каждую из двух подсистем (три графика - три варианта работы с памятью)).
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Паузы в работе программы вызываемые GC
« Ответ #40 : Март 01, 2012, 12:41:36 pm »
Кстати, можно ли смоделировать эту проблему/задачу в относительно маленьком куске кода для последующих экспериментов/бенчмарков?

Конечно можно. Я так и делал. Запускаешь два потока. Один поток мусорит. Второй поток делает Sleep(1) и измеряет сколько времени он на самом деле проспал, максимальное время спячки и будет максимальной паузой GC.

Программа работает очень долго, для построения одной "линии" я оставлял её на ночь:

namespace TestGCPause
{
   class Program
   {
      static void Main (string[] args)
      {
         using (OneTest x = new OneTest())
         {
            for (uint i = 1; i < 100; i++) // вычисляем 100 точек для одной "линии"
            {
               System.GC.Collect();
               x.Run((1000000 * i)*4);
            }
         }
      }
   }

   public sealed class OneTest: System.IDisposable
   {
      private bool isDisposed;
      private double maxPause;

      public OneTest ()
      {
         System.Threading.Thread thread = new System.Threading.Thread(this.GCLoop);
         thread.Start();
      }

      public void Run (uint objectCount)
      {
         this.maxPause = 0;
         long m0 = System.GC.GetTotalMemory(true);
         this.maxPause = 0;
         byte[][] a = new byte[objectCount][];
         ulong k = 127837817; // это быстрый генератор случайных чисел
         const ulong dk = 3746123567;
         for (int i = 0; i < 10; i++)
         {
            for (int j = 0; j < objectCount; j++)
            {
               k = k + dk;
               ulong size = 16 + (k % (64 - 16));
               //ulong size = 64 + (k % (256 - 64));
               //ulong size = 256 + (k % (1024 - 256));
               byte[] x = new byte[size];
               x[k % size] = (byte)k;
               a[k % objectCount] = x;
            }
         }
         long m1 = System.GC.GetTotalMemory(true);
         System.Console.Write("{" + (m1 - m0) + ", " + this.maxPause + "},");
         System.GC.KeepAlive(a);
      }

      private void GCLoop () // Этот поток измеряет паузы
      {
         while (!this.isDisposed)
         {
            System.DateTime t0 = System.DateTime.UtcNow;
            System.Threading.Thread.Sleep(1);
            System.DateTime t1 = System.DateTime.UtcNow;
            double dt = (t1 - t0).TotalSeconds;
            if (this.maxPause < dt)
            {
               this.maxPause = dt;
            }
         }
      }

      public void Dispose ()
      {
         this.isDisposed = true;
      }
   }
}

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Паузы в работе программы вызываемые GC
« Ответ #41 : Март 01, 2012, 01:03:39 pm »
Программа работает очень долго, для построения одной "линии" я оставлял её на ночь
Здесь я опубликовал вариант печатающий результат в консоль. Если собираетесь запускать её на ночь, то лучше пишите результат в файл (а то если кончится память программа упадёт и результат многочасовых вычислений пропадёт).

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #42 : Март 01, 2012, 01:15:54 pm »
Программа работает очень долго, для построения одной "линии" я оставлял её на ночь
Здесь я опубликовал вариант печатающий результат в консоль. Если собираетесь запускать её на ночь, то лучше пишите результат в файл (а то если кончится память программа упадёт и результат многочасовых вычислений пропадёт).
Ну, это ж одно и то же, просто перенаправлю выход stdout в файла и все. Типа "mono test.exe > logfile"
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #43 : Март 01, 2012, 01:33:03 pm »
А System.GC.Collect() обязательно вызывать? Оно ж во-первых не везде будет доступно, а во-вторых в случае инкрементального сборщика мусора особого смысла не имеет.
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Паузы в работе программы вызываемые GC
« Ответ #44 : Март 01, 2012, 02:13:51 pm »
Прочитал исходник, похоже это немного не то что я хотел. То есть это не моделирование задачи, а измерение конкретно сколько займет сборка мусора (разовая) когда намусорили вот столько-то (судя по System.GC.KeepAlive(a); ).

Я же хотел смоделировать предметную область где вырастает эта проблема.
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"