Как вы справляетесь с «событиями» в высокопроизводительном сценарии? - PullRequest
3 голосов
/ 27 декабря 2010

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

Действительно, я должен был все время подчеркивать, что меня больше интересует память накладные расходы TEventArgs аргументов в событиях .NETчем стоимость с точки зрения скорость .В конечном счете, я должен признать, что для всех практических целей - с точки зрения скорости памяти и - затраты незначительны.Тем не менее, я подумал, что было бы интересно увидеть, что повышение многих событий «обычным» способом стоит стоит что-то - и что в крайних случаях это может даже привести к поколению 1сборки мусора, которые могут иметь или не иметь значения в зависимости от ситуации (по моему опыту, чем больше должна быть система в реальном времени, тем важнее помнить, где создается мусор и как его минимизировать)при необходимости).


Это может показаться глупым вопросом.Я понимаю, что Windows Forms, например, можно легко считать «высокопроизводительным» сценарием, когда сотни или даже тысячи событий возникают очень быстро (например, событие Control.MouseMove) постоянно.Но все же я задаюсь вопросом, действительно ли разумно проектировать класс с событиями .NET, когда ожидается, что этот класс будет использоваться в высокопроизводительном, критичном ко времени коде.

Основная проблема, которую я имею, связана ссоглашение, что каждый использует что-то вроде EventHandler<TEventArgs> для всех событий, где TEventArgs происходит от EventArgs и, по всей вероятности, является классом, который должен создаваться каждый раз, когда событие вызывается / обрабатывается.(Если это просто EventsArgs, очевидно, это не тот случай, когда можно использовать EventArgs.Empty; но при условии, что любая значимая и непостоянная информация содержится в типе TEventArgs, создание экземпляра будетвероятно, понадобится.) Кажется, что это приводит к большему давлению ГХ, чем я ожидал бы для создания высокопроизводительной библиотеки.

Тем не менее, единственные альтернативы, о которых я могу подумать:

  1. Использование нетрадиционных типов делегатов (т. Е. Не EventHandler<TEventArgs>) для событий, получение только параметров, которые не требуют создания экземпляров объекта, таких как int, double и т. Д. (Даже string, и передача ссылокк существующим строковым объектам).
  2. Пропуск событий вообще и использование виртуальных методов, заставляя клиентский код переопределять их по желанию.Похоже, что это в основном тот же эффект, что и в предыдущей идее, но несколько более контролируемым образом.

Являются ли мои опасения по поводу давления GC событий .NET необоснованными с самого начала?Если так, что я там пропускаю?Или есть какая-то третья альтернатива, которая лучше, чем две, которые я только что перечислил?

Ответы [ 3 ]

4 голосов
/ 28 декабря 2010

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

Более того, объекты аргументов делегата всегда являются объектами поколения №0. Они не держатся достаточно долго, чтобы их повысили. Выделение и сбор мусора - это очень дешево. Это не настоящая проблема, не гонитесь за этим призраком.

3 голосов
/ 28 декабря 2010

Прежде чем что-то делать, вам следует подумать о том, чтобы выполнить какой-то уровень профилирования, чтобы убедиться, что это действительно представляет реальную проблему. В таких случаях, как движение мыши пользовательского интерфейса, события происходят с такой низкой частотой по сравнению к машине, которая обычно оказывает незначительное влияние на поведение ГХ. Имейте в виду, что .NET GC довольно хорошо собирает недолговечные объекты с небольшим количеством ссылок на них - это средние и долгоживущие объекты, на которые ссылаются во многих местах, которые могут создавать проблемы.

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

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

  2. Используйте реализацию TEventArgs flyweight , которая повторяет экземпляры аргумента события после использования. Это может быть сложным предложением, так как вам нужно убедиться, что обработчики событий никогда сохранить экземпляр параметра для использования в другом месте. Однако при правильной реализации этот шаблон может значительно сократить количество экземпляров облегченных типов, которые необходимо обрабатывать и собирать.

2 голосов
/ 27 декабря 2010

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

Например, в любом конкретном примере стековые выстрелы скажут вам, чего стоит быть обеспокоенным. Может случиться так, что затраты не столько на создание / уничтожение объектов событий, сколько на то, какую обработку они вызывают.

В наши дни проблема с производительностью, которую я часто вижу, это «убегающие уведомления». Событие может просто установить свойство объекта. Ничего страшного, правда? Но тогда этот параметр свойства может быть перехвачен суперклассом, который может искать коллекции, в которых находится объект, и отправлять уведомления о добавлении / удалении / перемещении объекта в этих коллекциях, что может привести к тому, что окна станут недействительными или пункты меню будут созданные / удаленные или элементы управления деревом для расширения / закрытия и т. д. ...

...