Ik heb een WPF applicatie die in principe gezien kan worden als een soort live powerpoint presentatie. Er is een hoofdscherm waarop de gebruiker verschillende elementen kan slepen die live data kunnen tonen, zoals text elementen. De gebruiker kan dus zijn eigen scherm ontwikkelen.
Nu wil ik graag meten hoe lang WPF nodig heeft om dit scherm te updaten elke keer als de achterliggende data een update krijgt.
Het systeem werkt eigenlijk vrij eenvoudig. Ik hou een lijstje bij met de text elementen die de gebruiker op zijn scherm heeft geplaatst. De text elementen zijn in de basis gewoon een TextBlock met een data binding op de Text property;
De Value property van alle text elementen krijgt ongeveer 30 keer per seconde een update vanuit de achterliggende live data (waar die vandaan komt lijkt me niet belangrijk hier). In een loop draai ik de volgende updater:
Kort gezegd, de Value property van de Binding class krijgt ongeveer 30 keer per seconde een nieuwe waarde. Via de INotifyPropertyChanged krijgt de UI vervolgens een update.
In de Update loop kan ik in principe de "FPS" meten van de update code. Dit geeft me het tijd verschil tussen twee updates. Dit zou ongeveer 30 moeten zijn maar in de praktijk is dat ~20-25, wat prima is.
In de UI kan echter nog een veel grotere lag zitten. Als er veel elementen zijn, of veel afbeeldingen, dan loopt de UI redelijk traag met een FPS die ik onder de 10 schat.
De vraag is nu - hoe kan ik deze FPS meten? De "data FPS" die ik nu al meet is leuk maar niet heel relevant, de UI FPS vind ik veel belangrijker om te meten en te laten zien.
Ik zie nu twee problemen:
1. Ik roep in een loop de UpdateValue aan voor elk text element, wat op zijn beurt een NotifyPropertyChanged aanroept en de UI gaat updaten. Maar eigenlijk is het onzin om dit voor elk text element apart te doen. Ik zou eigenlijk moeten wachten tot alle updates klaar zijn, en dan 1 keer de UI updaten.
Is WPF slim genoeg dat dit al vanzelf gebeurt? Of moet ik hier op een of andere manier rekening mee houden?
2. Ik kan niet vinden hoe ik de 'delay' kan meten die er zit tussen het eind van de Update loop, en de eerstvolgende UI refresh. Ik wil dus eigenlijk meten wat de update rate / fps van de UI is, maar ik zie geen manier om dat te doen.
Ik heb geprobeerd om de OnRender of LayoutUpdated events van de view te gebruiken, en daar de laatste update tijd op te slaan om te meten hoe lang er tussen twee updates zit. Maar WPF lijkt slim genoeg om niet constant te updaten en alleen als er daadwerkelijk iets veranderd. Deze FPS is dus niet heel relevant en varieert van ontzettend hoge waardes (als er paar sec niks veranderd) naar heel lage waardes (als er ineens meerdere updates in een korte tijd komen).
Eigenlijk twee vragen dus:
Kan ik dit proces optimalizeren?
Hoe kan ik meten hoe efficient het momenteel is (zodat ik weet of ik het daadwerkelijk aan het verbeteren ben)?
Nu wil ik graag meten hoe lang WPF nodig heeft om dit scherm te updaten elke keer als de achterliggende data een update krijgt.
Het systeem werkt eigenlijk vrij eenvoudig. Ik hou een lijstje bij met de text elementen die de gebruiker op zijn scherm heeft geplaatst. De text elementen zijn in de basis gewoon een TextBlock met een data binding op de Text property;
C#:
1
| <TextBlock Text="{Binding Value}" /> |
De Value property van alle text elementen krijgt ongeveer 30 keer per seconde een update vanuit de achterliggende live data (waar die vandaan komt lijkt me niet belangrijk hier). In een loop draai ik de volgende updater:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| private double _previousUpdate = 0; public void Update() { // Get bindings (cached after first run) var bindings = GetBindings(); // Update all foreach (var binding in bindings) { binding.UpdateValue(); } // Measure time var now = (double) DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond; FPS = 1d / (now - _previousUpdate); _previousUpdate = now; } private List<Binding> GetBindings() { ... } public class Binding : INotifyPropertyChanged { public string Value {get;set;} // notify property changed omitted public void UpdateValue() { // Set appropriate value, triggers property changed event, triggers UI update Value = ... } } |
Kort gezegd, de Value property van de Binding class krijgt ongeveer 30 keer per seconde een nieuwe waarde. Via de INotifyPropertyChanged krijgt de UI vervolgens een update.
In de Update loop kan ik in principe de "FPS" meten van de update code. Dit geeft me het tijd verschil tussen twee updates. Dit zou ongeveer 30 moeten zijn maar in de praktijk is dat ~20-25, wat prima is.
In de UI kan echter nog een veel grotere lag zitten. Als er veel elementen zijn, of veel afbeeldingen, dan loopt de UI redelijk traag met een FPS die ik onder de 10 schat.
De vraag is nu - hoe kan ik deze FPS meten? De "data FPS" die ik nu al meet is leuk maar niet heel relevant, de UI FPS vind ik veel belangrijker om te meten en te laten zien.
Ik zie nu twee problemen:
1. Ik roep in een loop de UpdateValue aan voor elk text element, wat op zijn beurt een NotifyPropertyChanged aanroept en de UI gaat updaten. Maar eigenlijk is het onzin om dit voor elk text element apart te doen. Ik zou eigenlijk moeten wachten tot alle updates klaar zijn, en dan 1 keer de UI updaten.
Is WPF slim genoeg dat dit al vanzelf gebeurt? Of moet ik hier op een of andere manier rekening mee houden?
2. Ik kan niet vinden hoe ik de 'delay' kan meten die er zit tussen het eind van de Update loop, en de eerstvolgende UI refresh. Ik wil dus eigenlijk meten wat de update rate / fps van de UI is, maar ik zie geen manier om dat te doen.
Ik heb geprobeerd om de OnRender of LayoutUpdated events van de view te gebruiken, en daar de laatste update tijd op te slaan om te meten hoe lang er tussen twee updates zit. Maar WPF lijkt slim genoeg om niet constant te updaten en alleen als er daadwerkelijk iets veranderd. Deze FPS is dus niet heel relevant en varieert van ontzettend hoge waardes (als er paar sec niks veranderd) naar heel lage waardes (als er ineens meerdere updates in een korte tijd komen).
Eigenlijk twee vragen dus:
Kan ik dit proces optimalizeren?
Hoe kan ik meten hoe efficient het momenteel is (zodat ik weet of ik het daadwerkelijk aan het verbeteren ben)?