GC с С# и С++ в том же решении
У меня есть решение, состоящее из нескольких проектов С#. Это было написано на С#, чтобы быстро запустить его. Сбор мусора начинает становиться проблемой: мы наблюдаем задержки в 100 мс, которых мы бы хотели избежать.
Одна мысль заключалась в том, чтобы переписать его на С++, проект по проекту. Но если вы объедините С# с неуправляемым С++, будут ли потоки в проектах на С++ заморожены сборками мусора?
UPDATE
Спасибо за ваши ответы. Это, по сути, приложение, где 100 мс могут быть значительными. Вероятно, это было плохое решение построить его на С#, но было важно, чтобы он быстро работал и работал быстро.
В настоящее время мы используем Windows Мультимедийные таймеры для запуска события каждые 5 мс. Мы видим пробелы в 100+ ms, и мы подтвердили, проверив счетчики GC, что они всегда возникают во время сбора. Оптимизация продолжается; встроенный режим Release.
Ответы
Ответ 1
Я работаю разработчиком .NET в торговой фирме, где, как и вы, мы заботимся о задержках в 100 мс. Сбор мусора действительно может стать серьезной проблемой, когда требуется надежная минимальная латентность.
Тем не менее, я не думаю, что переход на С++ станет умным шагом, главным образом из-за того, сколько времени это будет. Сбор мусора происходит после того, как определенное количество памяти было выделено в кучу с течением времени. Вы можете существенно уменьшить эту проблему, минимизируя количество кучи, которое создает ваш код.
Я бы рекомендовал попытаться определить методы в вашем приложении, которые отвечают за значительные объемы распределения. Объекты Anywhere собираются стать кандидатом на модификацию. Классический подход к борьбе с сборкой мусора использует пулы ресурсов: вместо создания нового объекта каждый раз, когда вызывается метод, поддерживайте пул уже построенных объектов, заимствуя из пула при каждом вызове метода и возвращая объект в пул после завершения метода.
Еще одна проблема состоит в том, чтобы искоренить любые ArrayList
, HashTable
или подобные не общие коллекции в вашем коде, что типы значений box/unbox, что приводит к совершенно ненужному распределению кучи. Замените их на List<T>
, Dictionary<TKey, TValue>
и т.д., Где это возможно (здесь я специально ссылаюсь на коллекции типов значений, таких как int
, double
, long
и т.д.). Аналогичным образом, обратите внимание на любые методы, которые вы можете вызывать, какие аргументы типа значения поля (или возвращать типы значений в боксе).
Это всего лишь несколько относительно небольших шагов, которые вы можете предпринять для сокращения количества сборов мусора, но они могут иметь большое значение. С достаточным усилием можно даже полностью (или, по крайней мере, почти) устранить коллекции all сборщиков мусора поколения 2 на фазе непрерывных операций (все, кроме запуска и выключения) вашего приложения. И я думаю, вы обнаружите, что коллекции поколения 2 - настоящие тяжелые нападающие.
Здесь представлен документ, в котором излагаются усилия одной компании по минимизации задержки в приложении .NET посредством пула ресурсов в дополнение к нескольким другим методам с большим успехом:
Быстрое добавление использует Microsoft.NET 3.5 Framework для создания обработки с наименьшей задержкой FIX и FAST p >
Итак, повторю: я бы настоятельно рекомендовал изучить способы изменения вашего кода, чтобы сократить сбор мусора при конвертировании на совершенно другой язык.
Ответ 2
Во-первых, вы пробовали профилировать вещи, чтобы узнать, можете ли вы оптимизировать использование памяти? Хорошее место для начала - это профайлер CLR (работает со всеми CLR до 3.5).
Переписывание всего на С++ является невероятно радикальным изменением только ради небольшого удара производительности - это похоже на исправление бумаги путем ампутации вашей руки.
Ответ 3
Вы уверены, что эти задержки в 100 мс обусловлены GC? Я бы сделал ОЧЕНЬ уверен, что GC действительно является вашей проблемой, прежде чем тратить много времени, усилий и денег, переписывая вещь на С++. Объединение управляемого кода с неуправляемым кодом также представляет свои проблемы, так как вам приходится иметь дело с сортировкой между этими двумя контекстами. Это добавит свой собственный поток производительности, и ваш чистый выигрыш вполне может оказаться в конечном итоге равным нулю.
Я бы прокомментировал ваше приложение С# и сузил место, где происходят ваши задержки на 100 мс. Этот инструмент может быть полезен:
Как использовать профилировщик CLR
Слово на GC
Еще одно слово о .NET GC (или, действительно, какой-либо GC, если на то пошло.) Этого недостаточно, чтобы сказать достаточно часто, но это критический фактор при успешном написании кода с GC:
Наличие сборщика мусора не означает, что вам не нужно думать об управлении памятью!
Написание оптимального кода, который отлично сочетается с GC, требует меньше усилий и хлопот, чем написание кода на С++, который отлично воспроизводится с неуправляемой кучей... но вам все равно нужно понять GC и написать код, который прекрасно сочетается с ним. Вы не можете полностью игнорировать все связанные с управлением памятью вещи. Вы должны беспокоиться об этом меньше, но вам все равно придется думать об этом. Написание кода, который отлично сочетается с GC, является критическим фактором в достижении кода исполнителей, который не создает проблемы управления памятью.
Следующая статья также должна быть полезна, так как она описывает фундаментальный принцип работы .NET GC (действительный через .NET 3.5... вполне вероятно, что эта статья больше не полностью применима для .NET 4.0, поскольку некоторые критические изменения в его GC... для одного, ему больше не нужно блокировать потоки .NET при сборке):
Сбор мусора: автоматическое управление памятью в Microsoft.NET Framework
Ответ 4
CLR GC не приостанавливает потоки, выполняющие неуправляемый код во время сбора. Если собственный код вызывает управляемый код или возвращается к управляемому коду, тогда на него может влиять коллекция (например, любой другой управляемый код).
Ответ 5
Если проблема с 100 мс, я понимаю, что ваш код критически важен. Смешивание управляемого и неуправляемого кода будет иметь накладные расходы interop для вызова между управляемым appdomain и неуправляемым пространством.
GC очень хорошо оптимизирован, поэтому перед тем, как сделать это, попробуйте профилировать свой код и реорганизовать его. Если вас беспокоит GC, попробуйте сыграть с настройкой приоритета потока и свести к минимуму создание объекта и кэшировать данные, когда это возможно. В свойстве проекта включен параметр Оптимизировать код.
Ответ 6
Одна мысль заключалась в том, чтобы переписать его на С++, проект по проекту. Но если вы объедините С# с неуправляемым С++, будут ли потоки в проектах на С++ заморожены сборками мусора?
Нет, если код С++ работает на разных потоках. куча С++ и управляемая куча - это разные вещи.
С другой стороны, если ваш код на С++ делает много нового/удалить, вы все равно начнете видеть ячейки сложения в коде С++, поскольку куча становится фрагментированной. И эти киоски, вероятно, будут намного хуже, чем то, что вы видите в коде С#, потому что нет GC. Когда куча должна быть очищена, это просто происходит в вызове нового или удаляется.
Если у вас действительно есть жесткие требования к производительности, тогда вам нужно планировать не выполнять выделение памяти из общей кучи внутри вашего критического кода времени. На практике это означает, что это будет больше похоже на C-код, чем на С++-код, или с использованием специальных пулов памяти и размещения новых.
Ответ 7
.NET 4.0 имеет то, что называется Background Garbage Collection, которое отличается от Concurrent Garbage Collection, что может быть причиной вашей проблемы. Джейсон Олсон рассказывает об этом с Карлом Франклин и Ричардом Кэмпбеллом на .NET Rocks Эпизод № 517. Вы можете просмотреть транскрипт здесь. Это на стр. 5.
Я не совсем уверен, что просто обновление до 4.0 Framework решит вашу проблему, но я думаю, что было бы хорошо посмотреть на это, прежде чем переписывать все на С++.