Конфликт потоков при выделении памяти - PullRequest
0 голосов
/ 11 мая 2018

В C # я запускаю игрушечный код, который создает много маленьких объектов (которых, я знаю, в идеале следует избегать - я просто хочу изучить проблему).При одинаковом общем количестве созданных объектов один поток работает быстрее, чем один поток на процессор (Parallel.For).

Атомарное действие состоит в создании списка (фактически массива), содержащего 20 тыс. Небольших объектов (здесь длинных[4] для простоты):

private static void CreateList()
{
    long[][] list = new long[20000][];
    for (var i = 0; i < 20000; i++)
        list[i] = new long[4];
}

Если я создаю 1000 списков в одном потоке, он запускается за 1,5 с.Если я создаю 1000 списков с несколькими потоками (каждый из которых отвечает за подмножество 1000 списков), он запускается за 2 с.

Поведение практически одинаково, когда:

  • с использованием классическогомаленькие объекты вместо длинных [4]
  • с использованием реального списка вместо массива
  • с использованием различного количества объектов

Не могли бы вы объяснить, почему?Есть ли в диспетчере памяти «блокировка».Это связано со сборкой мусора?

Подробности кода:

public static void Main()
{
    Benchmark(1000, CreateList);
}    

private static void Benchmark(int repeat, Action action)
{
    Console.WriteLine("Single thread");
    Benchmark(delegate ()
    {
        for (int i = 0; i < repeat; i++)
            action();
    });
    Console.WriteLine("Multi thread");
    Benchmark(delegate ()
    {
        Parallel.For(0, repeat, i => action());
    });
}

private static void Benchmark(Action action)
{
    for (int i = 0; i < 10; i++)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        action();
        sw.Stop();
        Console.WriteLine("Time : " + sw.Elapsed.TotalSeconds);
    }
}

1 Ответ

0 голосов
/ 14 мая 2018

Несмотря на то, что это нормально, менеджер памяти использует какой-то семафор, многопоточные приложения со многими выделениями памяти очень плохо работают со сборщиком мусора C # по умолчанию. С надлежащим сборщиком мусора дела обстоят НАМНОГО лучше.

Вы должны:

  • включить сервер GC
  • (возможно) отключить одновременный GC

Сервер GC обеспечит лучшую степень распараллеливания между потоками, поскольку распределение памяти частично независимо. В такой ситуации производительность может радикально измениться на машинах с несколькими ядрами.

Вкратце, добавьте это в файл конфигурации вашего проекта:

  <runtime>
    <gcServer enabled="true"/>
    <gcConcurrent enabled="false" />
  </runtime>

Подробнее о GC сервера и рабочей станции можно прочитать в Основы сборки мусора .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...