Reputation: 656
I need to optimize an rather big WPF application. We are getting data updates from the domain layer in a very high frequency. One idea to optimize the UI was to delay the update of some UI elements to improve the UI performance. Let's say we have a property "Counter" in the domain model that changes about 100 times per second. So we could create a timer that fires only once a second and updates an corresponding property in our ViewModel when the timer fires. That works pretty good so far. The timer would just copy the value from the domain object to the view model once a second and we're done.
Problem is: Each view model instance represents an entry in a list. And this list could have several thousand entries. With the solution I described we would also create several thousand timers. I would guess that creating so many timer instances is no good idea. A simple solution would be to just create a static timer instance in a base class or somewhere else and just use this single instance to update all view models. But that is not possible either. We have any different properties that should be updated independently from the domain models and each should have an idividual update rate.
Question 1: Does anyone have an idea how expensive a timer is? Does it really matter if I would create many thousand timers?
Question 2: Another soulution would be to create some sort of scheduler ... a kind of "MultiTimer" where I can register multiple timeouts. Does anyone have an suggestion on how to implement something like that?
Upvotes: 1
Views: 6546
Reputation: 16162
If you "create many thousand timers
" you will blow up the OS!. The timers callbacks runs in different threads or threadpool, so imagine you are creating a thousand of threads...
However the solution to your problem is depending on how you are getting the data from the domain layer at the first place. more information about that will helps.
Edit: You can assign index of update to each of your data changed. if your data is updating frequently in about 100ms
and you want each row to only updates after 1 second
, then use a Dictionary
or a ConcurrentDictionary
that hold each data and its update index int
"or you can use DataTime
instead if not all the data updating in 100ms
..":
private ConcurrentDictionary<Data, int> _dataUpdateFrequence = new ConcurrentDictionary<Data, int>();
private const int MaxFrequent = 10;
Data data = null;
private void OnDataUpdated(Data data)
{
int lastUpdateIndex = _dataUpdateFrequence.GetOrAdd(data, 0);
int newUpdateIndex = (lastUpdateIndex + 1) % MaxFrequent;
bool needsUpdate = lastUpdateIndex == 0 || newUpdateIndex == 0;
if (needsUpdate)
{
//Update the UI.
}
_dataUpdateFrequence.TryUpdate(data, newUpdateIndex , lastUpdateIndex);
}
Edit2: You stated at a comment:
I can not gauaranty that the domain object value is updated constantly. It might change a property 100 times in one second and then never again
In this case I would use DateTime
instead of int
, and so check if the last update was larger than 1 second then update the UI, else ignore. but here you need also to define a timer that will update all fields in each 30 second
for example. so no starvation to any of your updates "just in case for example some fields update in half a second and never updates again!" So:
private const int UpdateAllInterval = 30 * 1000;//30 seconds
private readonly TimeSpan MinimumIntervalToUpdate = new TimeSpan(0, 0, 1);
private ConcurrentDictionary<Data, DateTime> _dataUpdateFrequence = new ConcurrentDictionary<Data, DateTime>();
private System.Timers.Timer _updateTimer = new System.Timers.Timer();
//At the UI Constructor:
public Window..
{
_updateTimer.Interval = UpdateAllInterval;
_updateTimer.AutoReset = true;
_updateTimer.Elapsed += OnUpdateTimerElapced;
_updateTimer.Start();
}
private void OnUpdateTimerElapced(object sender, System.Timers.ElapsedEventArgs e)
{
this.Dispatcher.Invoke(/*update the UI method*/);//or this.Invoke if winforms
}
private void OnDataUpdated(Data data)
{
DateTime currentTime = DateTime.Now;
DateTime lastUpdateTime = _dataUpdateFrequence.GetOrAdd(data, currentTime);
bool needsUpdate = lastUpdateTime == currentTime ||
currentTime > lastUpdateTime.Add(MinimumIntervalToUpdate);
if (needsUpdate)
{
//Update the UI.
}
_dataUpdateFrequence.TryUpdate(data, currentTime, lastUpdateTime);
}
Upvotes: 2
Reputation: 73
Question1: Timer is always overhead for the existing application. It eats up the performance, especially your case with many thousand timers.
Question2: You can try Publish-Subscribe model, which is the best fit for your business scenario.
Upvotes: 0
Reputation: 10296
This could be implemented in a more elegant fashion using Microsoft's Reactive Extensions - in short Rx. Among other cool functionality Rx contains a Throttle Method that's well suited to tame aggressive event sources. Take a look here.
Upvotes: 2