Bas
Bas

Reputation: 27115

Unsubscribing from events - performance hit?

Consider the following code (from a performance report):

Performance report

This is part of a property notificiation listener component. The method OnItemPropertyChanged is a private instance-bound method with the PropertyChangedEventHandler signature. This method is called around 100.000 times and is causing significant delays in the application.

Are there performance considerations related to (un)subscribing events? Is there an explanation to why this would cause such a performance hit?

Upvotes: 20

Views: 2859

Answers (3)

Tigran
Tigran

Reputation: 62265

Pay attention that 94.2% is related to the relative execution time. So unsubscribing takes 94.2% of total execution time of Disable(..) method. And considering that other code raw are cast and null check, it's normal.

The real problem, imo (even if anything in regard of performance is strictly related to concrete execution context) is that this method is called 100.000 times.

Upvotes: 14

Bas
Bas

Reputation: 27115

With regards to the design flaw that many comments suggest: we have strongly opinioned users that will not sway from putting an object graph with 100k objects as a whole into the user interface; this is part of an ongoing improvement process and will hopefully be resolved in the future.

There was no significant difference between the sharedHandler and Method reference being passed to the unsubscription operator.

Using the weak event manager classes removes the performance hit; with or without creating delegates like Marc Gravell suggests. Perhaps this is related to this class creating its own event listener in a different way instead of using the one supplied in its argument. I have looked into the source but could not find an explanation as to why this pattern does seem to work fast (since the += and -= operators are still called).

Upvotes: 5

Marc Gravell
Marc Gravell

Reputation: 1064134

The first thing to note is that:

notificationItem.PropertyChanged -= OnItemPropertyChanged;

actually allocated a new delegate for the purpose. It also means that the equivalence test can't short-circuit at identity equivalence - it has to perform method/target equivalence (i.e. a different delegate instance, but same target/method, hence considered equivalent for the purposes of delegate combination).

What I would try first would be using a single delegate instance, i.e.

void OnItemPropertyChanged(object sender, PropertyChangedEventArgs args) {...}

private readonly PropertyChangedEventHandler sharedHandler;
public YourType() { // constructor
    sharedHandler = OnItemPropertyChanged;
}

Then when you subscribe, instead of:

notificationItem.PropertyChanged += OnItemPropertyChanged;

or

notificationItem.PropertyChanged -= OnItemPropertyChanged;

use instead:

notificationItem.PropertyChanged += sharedHandler;

or

notificationItem.PropertyChanged -= sharedHandler;

Worth a try, at least.

Upvotes: 16

Related Questions