Skip to content

Commit 4d104f0

Browse files
authored
Improve performance test. Measure FPS and average processing/renderin… (#424)
* Improve performance test. Measure FPS and average processing/rendering time. Fix #423
1 parent 3c59ba0 commit 4d104f0

File tree

1 file changed

+52
-20
lines changed

1 file changed

+52
-20
lines changed

rta/src/test/java/com/gluonhq/richtextarea/PerformanceTests.java

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.concurrent.CountDownLatch;
2020
import java.util.concurrent.ThreadLocalRandom;
2121
import java.util.concurrent.TimeUnit;
22+
import java.util.concurrent.atomic.AtomicInteger;
2223
import javafx.application.Platform;
2324
import javafx.scene.Scene;
2425
import javafx.scene.input.KeyCode;
@@ -39,10 +40,31 @@ public static void main(String[] args) throws InterruptedException {
3940
pt.insertMany();
4041
}
4142

43+
/**
44+
* This test measures performance of the combination {add character; render UI}
45+
* It is important to measure the combination, as a performance enhancements in
46+
* one part could lead to a degradation in the other part.
47+
* In this test, after a warmup of 1000 characters, we add 1000 characters (to the
48+
* already existing 1000 ones). After a character is added, a Pulse is requested.
49+
* Ideally, we render at 60 FPS and a character insert takes much less than 16ms,
50+
* so that not every requestPulse results in a separate pulse.
51+
* We use a prelayoutListener to keep track of how many pulses are executed, and use
52+
* that to calculate the FPS.
53+
* We also calculate the average time that it takes to add and render a character. This
54+
* number needs to be interpreted with caution, as it strongly depends on the performance of the
55+
* individual parts, and on the number of additions that can be done within 16ms.
56+
*
57+
* Note that currently lots of Nodes are created in both parts, and GC activity is expected,
58+
* which can influence the measured values.
59+
*
60+
* Run this test before and after a PR, to check for regression.
61+
* @throws InterruptedException
62+
*/
4263
public void insertMany() throws InterruptedException {
43-
44-
final int WARMUP_CNT = 100; // how many chars to warmup
45-
final int TEST_CNT = 100; // how many chars to test
64+
AtomicInteger pulseCounter = new AtomicInteger(0);
65+
Runnable prelayout = () -> pulseCounter.incrementAndGet();
66+
final int WARMUP_CNT = 1000; // how many chars to warmup
67+
final int TEST_CNT = 1000; // how many chars to test
4668
final int SLEEP_MS = 5000; // sleep between warmup and test
4769
CountDownLatch cdl = new CountDownLatch(1);
4870
Platform.startup(() -> {
@@ -63,7 +85,6 @@ public void insertMany() throws InterruptedException {
6385
ThreadLocalRandom tlr = ThreadLocalRandom.current();
6486
System.err.println("Will test inserts of " + TEST_CNT + " after warmup of " + WARMUP_CNT + " chars, and sleep of " + SLEEP_MS + " ms.");
6587

66-
long mem0 = getUsedMemory();
6788
long startTime = System.nanoTime();
6889
for (int i = 0; i < WARMUP_CNT; i++) {
6990
char c = (char) ('a' + tlr.nextInt(26));
@@ -72,7 +93,17 @@ public void insertMany() throws InterruptedException {
7293
}
7394
String k = String.valueOf(c);
7495
KeyEvent evt = new KeyEvent(KeyEvent.KEY_TYPED, k, "", KeyCode.UNDEFINED, false, false, false, false);
75-
Platform.runLater(() -> skin.keyTypedListener(evt));
96+
CountDownLatch testRun = new CountDownLatch(1);
97+
Platform.runLater(() -> {
98+
skin.keyTypedListener(evt);
99+
Platform.requestNextPulse();
100+
testRun.countDown();
101+
});
102+
boolean await = testRun.await(1, TimeUnit.SECONDS);
103+
if (!await) {
104+
System.err.println("ERROR warming up");
105+
System.exit(1);
106+
}
76107
}
77108
CountDownLatch cdl3 = new CountDownLatch(1);
78109
Platform.runLater(() -> cdl3.countDown());
@@ -82,6 +113,9 @@ public void insertMany() throws InterruptedException {
82113
System.err.println("warmup: total time = " + dur + ", average = " + (dur / (1e6 * WARMUP_CNT)) + ", now sleep for " + SLEEP_MS);
83114
Thread.sleep(SLEEP_MS);
84115
System.err.println("resume");
116+
pulseCounter.set(0);
117+
Platform.runLater(() -> rta.getScene().addPreLayoutPulseListener(prelayout));
118+
85119
startTime = System.nanoTime();
86120
final int cnt = TEST_CNT;
87121
for (int i = 0; i < cnt; i++) {
@@ -91,28 +125,26 @@ public void insertMany() throws InterruptedException {
91125
}
92126
String k = String.valueOf(c);
93127
KeyEvent evt = new KeyEvent(KeyEvent.KEY_TYPED, k, "", KeyCode.UNDEFINED, false, false, false, false);
94-
Platform.runLater(() -> skin.keyTypedListener(evt));
128+
CountDownLatch prodRun = new CountDownLatch(1);
129+
Platform.runLater(() -> {
130+
skin.keyTypedListener(evt);
131+
Platform.requestNextPulse();
132+
prodRun.countDown();
133+
});
134+
boolean await = prodRun.await(1, TimeUnit.SECONDS);
135+
if (!await) {
136+
System.err.println("ERROR running test");
137+
System.exit(1);
138+
}
95139
}
96140
CountDownLatch cdl4 = new CountDownLatch(1);
97141
Platform.runLater(() -> cdl4.countDown());
98142
cdl4.await(10, TimeUnit.SECONDS);
99143
endTime = System.nanoTime();
100144
dur = endTime - startTime;
101-
long mem1 = getUsedMemory();
102-
System.err.println("total time = " + dur + ", average = " + (dur / (1e6 * cnt)) + " and used mem = " + (mem1 - mem0));
145+
System.err.println("RefreshRate = "+1.e9*pulseCounter.get()/dur +" FPS");
146+
System.err.println("Average duration of a character addition = " + (dur / (1e6 * cnt))+"ms");
103147
Platform.exit();
104148
}
105149

106-
long getUsedMemory() {
107-
System.gc();
108-
try {
109-
Thread.sleep(50);
110-
} catch (InterruptedException ex) {
111-
System.getLogger(PerformanceTests.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
112-
}
113-
Runtime runtime = Runtime.getRuntime();
114-
long val = runtime.totalMemory() - runtime.freeMemory();
115-
return val;
116-
}
117-
118150
}

0 commit comments

Comments
 (0)