Reputation: 27115
Consider the following code (from a 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
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
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
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