יום חמישי, 5 ביולי 2012

How to improve WPF performance in the View model


When we found a performance issue in WPF,first we are trying to solve it in the the UI [XAML],
but what with the View Model?

There is more then one way to improve WPF performance in the View model,

I will present one of the major solution in this post.

So, how can we improve WPF performance in the View model site?
The idea is to reduce the number of times that NotifiyPropertyChanged event rasing,
by giving the view model the ability to set the rate of NotifiyPropertyChanged event.

Eample of standard work with View Model:

  1. We have a model (data producer) the raise same data changed event every ~5ms. 
  2. The view model catch the event and update it's property .
  3. If the current value in the view model equals to the new value then do nothing, else raise PropertyChanged event.
Standart view model with updates interval of 5ms

The results of this standart example :

 ( in "WPF Performance Suite")

Frame rate: 191 fps.
"Reports the rate at which the application is rendering to the screen"

"For applications without animation, this value should be near 0. During animations in a well-performing application, Frame Rate should be close to the monitor’s refresh rate (typically 60 or 75)".

Dirty Rect Addition rate 760rect/s

"A high value indicates that a lot of regions are changing. This isn't necessarily good or bad but a value to consider with the overall performance of your application."

WPF Performence suite   - Binding to standart view model



Ok, then how to reduce the frame rate?

I Create an IntervalViewModel that derived from BaseModel
The Interval View model can be set with the rate value by default with 30ms.

1. Each time the notify property event was fired, the property Name was collected to ConcuuentDictionary.
2. In timer tick, the NotyifyPropertyChanged event raises each property in the dictionary.




Set view model rate to 30ms results:

Frame rate: 42 fps.
Dirty Rect Addition rate  127rect/s




Set view model rate to 60 ms results:

Frame rate: 15 fps.
Dirty Rect Addition rate  63 rect/s


Base view model

   1: public class BaseViewModel : DisposableObject, INotifyPropertyChanged
   2: {
   3:     public event PropertyChangedEventHandler PropertyChanged = delegate { };
   4:  
   5:     /// <summary>
   6:     /// Raises the property changed.
   7:     /// </summary>
   8:     /// <param name="propertyName">Name of the property.</param>
   9:     /// <remarks></remarks>
  10:     protected virtual void RaisePropertyChanged(string propertyName)
  11:     {
  12:         InvokePropertyChangedEvent(propertyName);
  13:     }
  14:  
  15:     /// <summary>
  16:     /// Raises the property changed event.
  17:     /// </summary>
  18:     /// <param name="propertyName">Name of the property.</param>
  19:     /// <remarks></remarks>
  20:     private void InvokePropertyChangedEvent(string propertyName)
  21:     {
  22:         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  23:     }
  24: }

Interval view model 

   1: public class IntervalBaseViewModel : BaseViewModel
   2:    {
   3:        private readonly ConcurrentDictionary<string, DateTime> _properties;
   4:  
   5:        private DispatcherTimer dispatcherTimer;
   6:  
   7:  
   8:        /// <summary>
   9:        /// Initializes a new instance of the <see cref="IntervalBaseViewModel"/> class.
  10:        /// </summary>
  11:        /// <param name="rate">The rate.</param>      
  12:        public IntervalBaseViewModel(TimeSpan rate = new TimeSpan())
  13:        {
  14:            _properties = new ConcurrentDictionary<string, DateTime>();
  15:  
  16:            if (rate == default(TimeSpan))
  17:            {
  18:                rate =  TimeSpan.FromMilliseconds(30);
  19:            }
  20:            dispatcherTimer = new DispatcherTimer(DispatcherPriority.DataBind)
  21:            {
  22:                Interval = rate
  23:            };
  24:            dispatcherTimer.Tick += OnDispatcherTimerTick;
  25:            dispatcherTimer.Start();
  26:        }
  27:  
  28:        /// <summary>
  29:        /// Called when [dispatcher timer tick].
  30:        /// </summary>
  31:        /// <param name="sender">The sender.</param>
  32:        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  33:        /// <remarks></remarks>
  34:        void OnDispatcherTimerTick(object sender, EventArgs e)
  35:        {
  36:            ConcurrentDictionary<string, DateTime> properties = _properties;
  37:  
  38:            if (properties.IsEmpty) return;
  39:            
  40:  
  41:            foreach (var keyValuePair in properties)
  42:            {
  43:                DateTime dateTime;
  44:                _properties.TryRemove(keyValuePair.Key, out dateTime);
  45:  
  46:                base.RaisePropertyChanged(keyValuePair.Key);
  47:  
  48:            }
  49:        }
  50:  
  51:        /// <summary>
  52:        /// Raises the property changed.
  53:        /// </summary>
  54:        /// <param name="propertyName">Name of the property.</param>
  55:        /// <remarks></remarks>
  56:        protected override void RaisePropertyChanged(string propertyName)
  57:        {
  58:            _properties.TryAdd(propertyName, DateTime.Now);
  59:        }
  60:  
  61:        /// <summary>
  62:        /// Cleans up managed resources.
  63:        /// </summary>
  64:        /// <remarks></remarks>
  65:        protected override void CleanUpManagedResources()
  66:        {
  67:            base.CleanUpManagedResources();
  68:  
  69:            var timer = dispatcherTimer;
  70:            if (timer != null)
  71:            {
  72:                timer.Tick -= OnDispatcherTimerTick;
  73:                timer.Stop();               
  74:                dispatcherTimer = null;
  75:            }
  76:  
  77:            ConcurrentDictionary<string, DateTime> concurrentDictionary = _properties;
  78:  
  79:            if (concurrentDictionary != null)
  80:            {
  81:                concurrentDictionary.Clear();
  82:            }
  83:        }
  84:    }

  

Summary

As you can see, we can improve the application performence by the view model. :-)


By Morad Kobi.




אין תגובות:

הוסף רשומת תגובה