Масштабируемость сборщика мусора .NET 4

Недавно я сравнивал сборщик мусора .NET 4, интенсивно выделяя его из нескольких потоков. Когда выделенные значения были записаны в массиве, я не наблюдал масштабируемости, как я и ожидал (поскольку система поддерживает синхронный доступ к совместно используемому старому поколению). Однако, когда выделенные значения были немедленно отброшены, я был в ужасе, чтобы не наблюдать никакой масштабируемости тогда!

Я ожидал, что временный случай будет масштабироваться почти линейно, потому что каждый поток должен просто стереть питомник gen0 clean и начать снова, не сражаясь за какие-либо общие ресурсы (ничего не сохранится для старых поколений и не хватает промахов в L2, потому что gen0 легко вписывается в кеш L1).

Например, в этой статье MSDN говорится:

Распределения без синхронизации В многопроцессорной системе генерация 0 управляемой кучи разделяется на несколько областей памяти, используя одну арену на поток. Это позволяет нескольким потокам делать выделения одновременно, поэтому исключительный доступ к куче не требуется.

Может ли кто-нибудь подтвердить мои выводы и/или объяснить это несоответствие между моими прогнозами и наблюдениями?

5 ответов

Не полный ответ на вопрос, а просто для устранения некоторых заблуждений:.NET GC работает только параллельно в режиме рабочей станции. В режиме сервера он использует глобальный параллельный GC. Подробнее здесь. Отдельные питомники в .NET прежде всего должны избегать синхронизации при распределении; они все же являются частью глобальной кучи и не могут быть собраны отдельно.


Не уверен, что это такое и что вы видели на своей машине. Однако на вашей машине есть две разные версии CLR. Mscorwks.dll и mscorsvc.dll. Первый - это тот, который вы получаете, когда запускаете свою программу на рабочей станции, а второй - на одной из серверных версий Windows (например, Windows 2003 или 2008).

Версия рабочей станции относится к вашему локальному ПК, она не сожрает все ресурсы машины. Вы все еще можете читать свою электронную почту, пока идет GC. Версия сервера оптимизирована для масштабирования на аппаратном уровне сервера. Много оперативной памяти (GC не сильно ускоряет работу) и много ядер процессора (мусор собирается на несколько ядер). Вероятно, ваша цитированная статья рассказывает о версии сервера.

Вы можете выбрать версию сервера на своей рабочей станции, используйте элемент в вашем файле .config.


Я могу опасаться нескольких догадок о том, что происходит.

(1) Если у вас есть один поток, и в гене 0 нет свободного пространства M, GC будет работать только после того, как будут выделены M байты.

(2) Если у вас есть N потоков, и GC делит генерацию 0 на N/M пространство на поток, GC будет работать каждый раз, когда поток распределяет N/M байты. Здесь показано, что GC должен "остановить мир" (т.е. Приостановить все запущенные потоки), чтобы отмечать ссылки из наборов корней потоков. Это не дешево. Таким образом, не только GC будет работать чаще, он будет делать больше работы над каждой коллекцией.

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

Я не думаю, что это проблема .NET GC, а проблема с GC вообще. Один из коллег провел простой тест "пинг-понг", отправляющий простые целочисленные сообщения между двумя потоками с использованием SOAP. Тест выполнялся в два раза быстрее, когда два потока были в отдельных процессах, потому что распределение памяти и управление были полностью развязаны!


Очень быстро, легко видеть (прямо в корне, присваивая нули) и массивные релизы могут обмануть GC в нетерпение, и вся идея куки-локальной кучи - это хороший сон:-) Даже если бы у вас была полностью разделенная нить Локальные кучи (которые вы не знаете), таблица указателей ручек все равно должна быть полностью волатильной, чтобы сделать безопасным для общих сценариев с несколькими процессорами. О, и помните, что есть много потоков, кэш центрального процессора является общим, ядро ​​нуждается в приоритете, так что это не все только для вас:-)

Также будьте осторожны, что "куча" с двойными указателями состоит из двух частей - блока памяти и таблицы указателей ручек (так что блоки могут перемещаться, но ваш код всегда имеет один адрес). Такая таблица является критическим, но очень скудным ресурсом уровня процесса и почти единственным способом подчеркнуть ее - наполнить ее массивными быстрыми выпусками - так вам удалось это сделать: -))

В общем правило GC - утечка:-) Не навсегда, конечно, но вроде как можно долго. Если вы помните, как люди ходят, говоря "не заставляйте коллекцию GC"? Это часть истории. Кроме того, коллекция "stop the world" на самом деле намного эффективнее, чем "параллельная" и известна более красивым названием кражи цикла или сотрудничества с планировщиком. Только фазе метки необходимо заморозить планировщик, а на сервере есть пакет из нескольких потоков (N ядер в любом случае простаивает:-) Единственная причина для другой - это то, что он может делать операции в реальном времени, такие как воспроизведение видео, jittery, так же как и более длинный квант потока.

Итак, если вы собираетесь конкурировать с инфраструктурой на коротких и частых всплесках процессора (небольшое выделение, практически без работы, быстрый выпуск), единственное, что вы увидите/измеряете, это шум GC и JIT.

Если это было для чего-то реального, т.е. не просто экспериментирования, лучше всего использовать большие массивы значений в стеке (structs). Они не могут быть принуждены к куче и локальны, как может получить локальный, и не подвержены какому-либо обратному движению = > кэш должен любить их:-) Это может означать переход в "небезопасный" режим с использованием обычных указателей и, возможно, (если **** нужно что-то простое, как списки), но это небольшая цена, чтобы заплатить за выталкивание GC:-) Попытка заставить данные в кеш также зависит от того, как ваши стопки опираются на другое - вспомните, что вы не одиноки. Также давая вашим потокам некоторую работу, которая стоит, по крайней мере, нескольких квантов, которые могут быть связаны с выпусками. В худшем случае сценарий был бы, если вы выделите и освободите в кванте подписки.


или объяснить это несоответствие между моими прогнозами и наблюдениями?

Бенчмаркинг трудный. Бенчмаркинг подсистемы, которая не под вашим полным контролем, еще сложнее.

licensed under cc by-sa 3.0 with attribution.