GC с C# и C++ в том же решении

У меня есть решение, состоящее из многих проектов C#. Это было записано в C# для получения его операционный быстро. Сборки "мусора" начинают становиться проблемой — мы видим задержки приблизительно на 100 мс, которых мы хотели бы избежать.

Одна мысль состояла в том, чтобы переписать его в C++, проекте проектом. Но если Вы комбинируете C# с неуправляемым C++, будут потоки в проектах C++ также быть замороженными сборками "мусора"?

ОБНОВЛЕНИЕ

Спасибо за Ваши ответы. Это - на самом деле, приложение, где 100 мс могли бы быть значительными. Это было, вероятно, плохое решение создать его в C#, но было важно, чтобы это было в порядке быстро в то время.

Прямо сейчас мы используем Мультимедийные Таймеры Windows для увольнения события каждые 5 мс. Мы действительно видим приблизительно 100 + разрывы мс, и мы подтвердили путем проверки счетчиков GC, что они всегда происходят во время набора. Оптимизация идет; созданный в режиме Release.

14
задан Michael Covelli 2 March 2010 в 22:27
поделиться

7 ответов

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

Тем не менее, я не думаю, что переход на C++ будет разумным шагом, в основном из-за того, насколько трудоемким он будет. Сборка мусора происходит после того, как определенное количество памяти было выделено в куче с течением времени. Вы можете существенно смягчить эту проблему, минимизировав объем выделения кучи, который создает ваш код.

Я бы рекомендовал попытаться выявить методы в вашем приложении, которые отвечают за значительные объемы выделения. Любое место, где конструируются объекты, будет кандидатом на модификацию. Классический подход к борьбе со сбором мусора - использование пулов ресурсов: вместо того чтобы создавать новый объект при каждом вызове метода, поддерживайте пул уже созданных объектов, заимствуя из пула при каждом вызове метода и возвращая объект в пул после завершения метода.

Еще один простой способ заключается в поиске любых ArrayList, HashTable или подобных негенерических коллекций в вашем коде, которые включают/выключают типы значений, что приводит к совершенно ненужному выделению кучи. Замените их на List, Dictionary и т.д. везде, где это возможно (здесь я имею в виду именно коллекции типов значений, таких как int, double, long и т.д.). Аналогичным образом, обратите внимание на любые методы, которые вы вызываете и которые возвращают аргументы типа box (или возвращают boxed типы значений).

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

Вот документ, описывающий усилия одной компании по минимизации задержек в приложении .NET с помощью объединения ресурсов, в дополнение к нескольким другим методам, с большим успехом:

Rapid Addition использует Microsoft .NET 3.5 Framework для создания обработки FIX и FAST со сверхнизкой задержкой

Итак, повторюсь: Я бы настоятельно рекомендовал исследовать способы модификации вашего кода, чтобы сократить сборку мусора, а не переходить на совершенно другой язык.

6
ответ дан 1 December 2019 в 13:08
поделиться

Вы уверены, что эти 100 мс задержки связаны с GC? Я бы ОЧЕНЬ убедился, что сборщик мусора действительно является вашей проблемой, прежде чем вы потратите много времени, усилий и денег на то, чтобы переписать это на C ++. Комбинирование управляемого кода с неуправляемым кодом также создает свои собственные проблемы, поскольку вам приходится иметь дело с маршалингом между этими двумя контекстами. Это добавит собственного расхода производительности, и ваша чистая прибыль, скорее всего, в конечном итоге может оказаться нулевой.

Я бы профилировал ваше приложение C # и сузил точную информацию о причинах задержки в 100 мс. Этот инструмент может быть полезен:

Как: использовать CLR Profiler

Несколько слов о GC

Еще несколько слов о .NET GC (или вообще о любом GC, если на то пошло). Об этом почти не говорится достаточно часто, но это критический фактор для успешного написания кода с помощью GC:

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

Написание оптимального кода, который хорошо работает с GC, требует меньше усилий и хлопот, чем написание кода C ++, который хорошо работает с неуправляемой кучей ... но вам все равно нужно понимать GC и писать код, который хорошо работает с Это. Нельзя полностью игнорировать все вещи, связанные с управлением памятью. Вы должны меньше беспокоиться об этом, но вы все равно должны думать об этом. Написание кода, который хорошо работает с GC, является критически важным фактором в достижении производительности кода, который не СОЗДАЕТ проблемы с управлением памятью.

Следующая статья также должна быть полезной, поскольку в ней излагаются основные принципы поведения .NET GC (действительны до .NET 3.5 ... весьма вероятно, что эта статья больше не полностью пригодна для .NET 4.0, поскольку раньше некоторые критические изменения в его GC ... например, ему больше не нужно блокировать потоки .NET во время сбора):

Сборка мусора: автоматическое управление памятью в Microsoft .NET Framework

4
ответ дан 1 December 2019 в 13:08
поделиться

Во-первых, пробовали ли вы профилировать вещи, чтобы посмотреть, можете ли вы оптимизировать использование памяти? Хорошее место для начала - CLR profiler (работает со всеми CLR до 3.5).

Переписывать все на C++ - это невероятно радикальное изменение только ради небольшого увеличения производительности - это все равно, что лечить порез бумаги, ампутируя руку.

4
ответ дан 1 December 2019 в 13:08
поделиться

Сборщик мусора CLR не приостанавливает потоки, выполняющие неуправляемый код во время сбора. Если собственный код вызывает управляемый код или возвращается к управляемому коду, то на него может повлиять коллекция (как и на любой другой управляемый код).

3
ответ дан 1 December 2019 в 13:08
поделиться

Если 100 мс - проблема, думаю, ваш код критически важен. Смешивание управляемого и неуправляемого кода будет иметь накладные расходы на взаимодействие между управляемым доменом приложения и неуправляемым пространством.

Сборщик мусора очень хорошо оптимизирован, поэтому перед этим попробуйте профилировать свой код и провести его рефакторинг. Если вас беспокоит сборщик мусора, попробуйте поиграть с установкой приоритета потока и минимизировать создание объектов и, по возможности, кэшировать данные. В свойстве проекта также включен параметр Оптимизировать код.

1
ответ дан 1 December 2019 в 13:08
поделиться

.NET 4.0 имеет так называемую фоновую сборку мусора , которая отличается от параллельной сборки мусора , что может быть причиной вашей проблемы. Джейсон Олсон говорит об этом с Карлом Франклином и Ричардом Кэмпбеллом в .NET Rocks , эпизод # 517 . Вы можете просмотреть стенограмму здесь . Он находится на странице 5.

Я не совсем уверен, что простое обновление до 4.0 Framework решит вашу проблему, но я полагаю, что стоит потратить время на изучение этого вопроса, прежде чем переписывать все на C ++.

1
ответ дан 1 December 2019 в 13:08
поделиться

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

Нет, если код C ++ выполняется в разных потоках. куча C ++ и управляемая куча - это разные вещи.

С другой стороны, если ваш код C ++ выполняет много операций new / delete, вы все равно начнете видеть остановки выделения в коде C ++ по мере фрагментации кучи. И эти задержки, вероятно, будут намного хуже, чем то, что вы видите в коде C #, потому что там нет GC.Когда нужно очистить кучу, это просто происходит внутри вызова new или delete.

Если у вас действительно жесткие требования к производительности, вам нужно спланировать, чтобы не выполнять никакого выделения памяти из общей кучи внутри вашего критичного по времени кода. На практике это означает, что это будет больше похоже на код C, чем на код C ++, или использование специальных пулов памяти и размещение new.

1
ответ дан 1 December 2019 в 13:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: