In mijn applicatie kunnen gebruikers hun eigen scripts schrijven, welke ongeveer 30 keer per seconde uitgevoerd worden. De scripts moeten noodzakelijk op de UI thread uitgevoerd worden omdat ze direct met de UI moeten communiceren. Het is de bedoeling dat de scripts zeer simpel en snel zijn zodat er geen "lag" in de UI optreedt. Dat wil zeggen een paar regels code om een getal naar een string te converteren, kleur van een object te veranderen, positie van een object te veranderen, etc.
Ik wil de gebruiker graag nauwkeurige feedback geven over hoe lang het script nodig heeft om uit te voeren. Als ze dan lag merken weten ze welk script ze moeten verbeteren.
Momenteel doe ik dit met een Stopwatch, de manier die ik op letterlijk elk topic op het internet kan vinden:
Dit werkt prima, op een groot probleem na: als de UI iets anders aan het doen is (dat compleet ongerelateerd kan zijn aan de scripts) en enige tijd nodig heeft, dan is die tijd terug te zien in de execution time van de scripts. Stel dat de gebruiker een knop indrukt welke op de achtergrond een file leest ofzo, dit duurt toch al gauw weer een paar ms en de script execution time schiet enorm omhoog (van enkele microseconden tot ~milliseconden).
Ik kan niet helemaal verklaren waarom dit zo is aangezien de stopwatch toch echt enkel de ExecuteScript method zou moeten timen? Maar het is zeer irritant omdat om de haverklap de scripts heel traag lijken te lopen, ook al had het niks met het script te maken maar met iets compleet anders.
In een simpele test applicatie kan ik het probleem eenvoudig reproduceren:
- Een continue loop op een background thread met 250ms sleep
- Elke iteratie roept de loop een method op de UI thread aan die een 10 ms sleep doet (om een script na te doen)
- Een knop welke een seconde sleep doet, om een ongerelateerd iets na te bootsen wat flink wat tijd kost.
Zolang ik de knop niet aanraak duurt de "WorkOnUiThread" ongeveer 10 ms (af en toe kleine uitschieters). Zodra ik op de knop klik duurt de eerstvolgende iteratie ongeveer 800 ms (ik gok: 1 sec - 250 ms?).
Hoe kan ik de tijd van enkel de WorkOnUiThread method nauwkeurig timen, zelfs als de UI door iets anders tijdelijk vast loopt? Is dit uberhaupt mogelijk?
Ik wil de gebruiker graag nauwkeurige feedback geven over hoe lang het script nodig heeft om uit te voeren. Als ze dan lag merken weten ze welk script ze moeten verbeteren.
Momenteel doe ik dit met een Stopwatch, de manier die ik op letterlijk elk topic op het internet kan vinden:
C#:
1
2
3
4
5
| var sw = new Stopwatch(); sw.Start(); ExecuteScript(); sw.Stop(); ReportExecutionTime($"Elapsed: {sw.ElapsedTicks/10d} us"); |
Dit werkt prima, op een groot probleem na: als de UI iets anders aan het doen is (dat compleet ongerelateerd kan zijn aan de scripts) en enige tijd nodig heeft, dan is die tijd terug te zien in de execution time van de scripts. Stel dat de gebruiker een knop indrukt welke op de achtergrond een file leest ofzo, dit duurt toch al gauw weer een paar ms en de script execution time schiet enorm omhoog (van enkele microseconden tot ~milliseconden).
Ik kan niet helemaal verklaren waarom dit zo is aangezien de stopwatch toch echt enkel de ExecuteScript method zou moeten timen? Maar het is zeer irritant omdat om de haverklap de scripts heel traag lijken te lopen, ook al had het niks met het script te maken maar met iets compleet anders.
In een simpele test applicatie kan ik het probleem eenvoudig reproduceren:
- Een continue loop op een background thread met 250ms sleep
- Elke iteratie roept de loop een method op de UI thread aan die een 10 ms sleep doet (om een script na te doen)
- Een knop welke een seconde sleep doet, om een ongerelateerd iets na te bootsen wat flink wat tijd kost.
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
| public MainWindow() { InitializeComponent(); var t = new Thread(Loop); t.Start(); } private void Loop() { while (true) { var sw = new Stopwatch(); sw.Start(); Dispatcher.Invoke(WorkOnUiThread); sw.Stop(); Debug.WriteLine($"Elapsed: {sw.ElapsedMilliseconds:0.0} ms"); Thread.Sleep(250); } } private void WorkOnUiThread() { Thread.Sleep(10); } private void Button_Click(object sender, RoutedEventArgs e) { Thread.Sleep(1000); } |
Zolang ik de knop niet aanraak duurt de "WorkOnUiThread" ongeveer 10 ms (af en toe kleine uitschieters). Zodra ik op de knop klik duurt de eerstvolgende iteratie ongeveer 800 ms (ik gok: 1 sec - 250 ms?).
Hoe kan ik de tijd van enkel de WorkOnUiThread method nauwkeurig timen, zelfs als de UI door iets anders tijdelijk vast loopt? Is dit uberhaupt mogelijk?