Skip to content

Commit 1075435

Browse files
committed
#139 add percentile implementation
1 parent 50f2f17 commit 1075435

File tree

5 files changed

+148
-6
lines changed

5 files changed

+148
-6
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ So for every line the text is extracted (not always every part of it). This allo
113113
a user which is familiar with the text log files to find out more details about
114114
the events that occurred.
115115

116+
### median, 75th...
117+
These columns show the median, 75th percentile etc.
118+
116119
### Gc pauses
117120
This are shows all stop-the-world pauses, that are not full gc pauses.
118121

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.tagtraum.perf.gcviewer.math;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
7+
/**
8+
* Extends {@link DoubleData} with the functionality to calculate percentiles.
9+
*/
10+
public class DoubleDataPercentile extends DoubleData {
11+
private List<Double> doubleSet = new ArrayList<>();
12+
private boolean isSorted = false;
13+
14+
@Override
15+
public void add(double x) {
16+
super.add(x);
17+
doubleSet.add(x);
18+
isSorted = false;
19+
}
20+
21+
/**
22+
* return the n-th percentile of the list.
23+
* @param percentile percentile as floating point number (median = 50.0)
24+
* @return value at n-th percentile
25+
*/
26+
public double getPercentile(double percentile) {
27+
// https://matheguru.com/stochastik/quantil-perzentil.html
28+
if (!isSorted) {
29+
Collections.sort(doubleSet);
30+
}
31+
32+
if (percentile < 10) {
33+
percentile = 10;
34+
}
35+
else if (percentile > 100) {
36+
percentile = 100;
37+
}
38+
39+
double position = percentile / 100 * doubleSet.size();
40+
if ((position == Math.rint(position)) && !Double.isInfinite(position)) {
41+
position = (position + position + 1) / 2;
42+
} else {
43+
position = Math.ceil(position);
44+
}
45+
return doubleSet.get((int)position-1);
46+
}
47+
}

src/main/java/com/tagtraum/perf/gcviewer/model/GCModel.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.logging.Logger;
2626

2727
import com.tagtraum.perf.gcviewer.math.DoubleData;
28+
import com.tagtraum.perf.gcviewer.math.DoubleDataPercentile;
2829
import com.tagtraum.perf.gcviewer.math.IntData;
2930
import com.tagtraum.perf.gcviewer.math.RegressionLine;
3031
import com.tagtraum.perf.gcviewer.model.AbstractGCEvent.CollectionType;
@@ -77,7 +78,7 @@ public class GCModel implements Serializable {
7778
private DoubleData fullGCPause;
7879
private double lastFullGcPauseTimeStamp = 0;
7980
private DoubleData fullGcPauseInterval; // interval between two stop the Full GC pauses
80-
private DoubleData gcPause; // not full gc but stop the world pause
81+
private DoubleDataPercentile gcPause; // not full gc but stop the world pause
8182
private DoubleData vmOperationPause; // "application stopped"
8283
private double lastGcPauseTimeStamp = 0;
8384
private DoubleData pauseInterval; // interval between two stop the world pauses
@@ -203,7 +204,7 @@ public GCModel() {
203204
this.totalPause = new DoubleData();
204205
this.fullGCPause = new DoubleData();
205206
this.fullGcPauseInterval = new DoubleData();
206-
this.gcPause = new DoubleData();
207+
this.gcPause = new DoubleDataPercentile();
207208
this.vmOperationPause = new DoubleData();
208209
this.pauseInterval = new DoubleData();
209210
this.initiatingOccupancyFraction = new DoubleData();
@@ -246,8 +247,8 @@ public URL getURL() {
246247
}
247248

248249
private void printPauseMap(Map<String, DoubleData> pauseMap) {
249-
for (Map.Entry<String, DoubleData> entry : pauseMap.entrySet()) {
250-
System.out.println(entry.getKey() + " [n, avg, sum, min, max]:\t" + entry.getValue().getN() + "\t" + entry.getValue().average() + "\t" + entry.getValue().getSum() + "\t" + entry.getValue().getMin() + "\t" + entry.getValue().getMax());
250+
for (Map.Entry<String, DoubleData> entry: pauseMap.entrySet()) {
251+
System.out.println(entry.getKey() + " [n, avg, sum, min, max, 50th, 75th, 95th, 99th]:\t" + entry.getValue().getN() + "\t" + entry.getValue().average() + "\t" + entry.getValue().getSum() + "\t" + entry.getValue().getMin() + "\t" + entry.getValue().getMax() + "\t" + ((DoubleDataPercentile)entry.getValue()).getPercentile(50)+ "\t" + ((DoubleDataPercentile)entry.getValue()).getPercentile(75)+ "\t" + ((DoubleDataPercentile)entry.getValue()).getPercentile(95)+ "\t" + ((DoubleDataPercentile)entry.getValue()).getPercentile(99));
251252
}
252253
}
253254

@@ -401,7 +402,7 @@ public Iterator<GCEvent> getFullGCEvents() {
401402
private DoubleData getDoubleData(String key, Map<String, DoubleData> eventMap) {
402403
DoubleData data = eventMap.get(key);
403404
if (data == null) {
404-
data = new DoubleData();
405+
data = new DoubleDataPercentile();
405406
eventMap.put(key, data);
406407
}
407408

src/main/java/com/tagtraum/perf/gcviewer/view/ModelDetailsPanel.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import java.util.Map;
1010
import java.util.Map.Entry;
1111
import java.util.Set;
12-
1312
import javax.swing.BorderFactory;
1413
import javax.swing.JLabel;
1514
import javax.swing.JPanel;
@@ -19,6 +18,7 @@
1918
import javax.swing.table.TableModel;
2019

2120
import com.tagtraum.perf.gcviewer.math.DoubleData;
21+
import com.tagtraum.perf.gcviewer.math.DoubleDataPercentile;
2222
import com.tagtraum.perf.gcviewer.model.GCModel;
2323
import com.tagtraum.perf.gcviewer.util.LocalisationHelper;
2424

@@ -334,6 +334,12 @@ private List<String> createColumnNamesList() {
334334
columnNames.add(LocalisationHelper.getString("data_panel_details_max"));
335335
columnNames.add(LocalisationHelper.getString("data_panel_details_avg"));
336336
columnNames.add(LocalisationHelper.getString("data_panel_details_stddev"));
337+
columnNames.add("median");
338+
columnNames.add("75th");
339+
columnNames.add("95th");
340+
columnNames.add("99th");
341+
columnNames.add("99.5th");
342+
columnNames.add("99.9th");
337343
columnNames.add(LocalisationHelper.getString("data_panel_details_sum"));
338344
columnNames.add(LocalisationHelper.getString("data_panel_details_sum_percent"));
339345

@@ -391,6 +397,12 @@ private List<List<String>> createDataList(Map<String, DoubleData> model, double
391397
entryList.add(pauseFormatter.format(entry.getValue().getMax()));
392398
entryList.add(pauseFormatter.format(entry.getValue().average()));
393399
entryList.add(pauseFormatter.format(entry.getValue().standardDeviation()));
400+
entryList.add(pauseFormatter.format(((DoubleDataPercentile)entry.getValue()).getPercentile(50)));
401+
entryList.add(pauseFormatter.format(((DoubleDataPercentile)entry.getValue()).getPercentile(75)));
402+
entryList.add(pauseFormatter.format(((DoubleDataPercentile)entry.getValue()).getPercentile(95)));
403+
entryList.add(pauseFormatter.format(((DoubleDataPercentile)entry.getValue()).getPercentile(99)));
404+
entryList.add(pauseFormatter.format(((DoubleDataPercentile)entry.getValue()).getPercentile(99.5)));
405+
entryList.add(pauseFormatter.format(((DoubleDataPercentile)entry.getValue()).getPercentile(99.9)));
394406
entryList.add(pauseFormatter.format(entry.getValue().getSum()));
395407
entryList.add(percentFormatter.format(entry.getValue().getSum() / totalSum * 100));
396408

@@ -406,6 +418,12 @@ private List<List<String>> createDataList(Map<String, DoubleData> model, double
406418
totalList.add("");
407419
totalList.add("");
408420
totalList.add("");
421+
totalList.add("");
422+
totalList.add("");
423+
totalList.add("");
424+
totalList.add("");
425+
totalList.add("");
426+
totalList.add("");
409427
totalList.add(pauseFormatter.format(totalSum));
410428
totalList.add(showPercentOfTotalPause ? percentFormatter.format(totalSum / totalPause * 100) : "");
411429

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.tagtraum.perf.gcviewer.math;
2+
3+
import static org.hamcrest.Matchers.closeTo;
4+
import static org.junit.Assert.assertThat;
5+
6+
import org.junit.Before;
7+
import org.junit.Test;
8+
9+
/**
10+
* @author <a href="mailto:[email protected]">Joerg Wuethrich</a> <p>created on: 15.07.2015</p>
11+
*/
12+
public class TestDoubleDataPercentile {
13+
private DoubleDataPercentile ddpInteger;
14+
private DoubleDataPercentile ddp;
15+
16+
17+
@Before
18+
public void setup() throws Exception {
19+
ddpInteger = new DoubleDataPercentile();
20+
ddpInteger.add(15);
21+
ddpInteger.add(20);
22+
ddpInteger.add(35);
23+
ddpInteger.add(40);
24+
ddpInteger.add(50);
25+
26+
ddp = new DoubleDataPercentile();
27+
ddp.add(1.5);
28+
ddp.add(2.3);
29+
ddp.add(3.5);
30+
ddp.add(4.1);
31+
ddp.add(5.2);
32+
}
33+
34+
@Test
35+
public void integerZero() throws Exception {
36+
assertThat("integer 10 percentile", ddpInteger.getPercentile(10), closeTo(15, 0.1));
37+
}
38+
39+
@Test
40+
public void integerFifty() throws Exception {
41+
assertThat("integer 50 percentile", ddpInteger.getPercentile(50), closeTo(35, 0.1));
42+
}
43+
44+
@Test
45+
public void integerSeventyfive() throws Exception {
46+
assertThat("integer 75 percentile", ddpInteger.getPercentile(75), closeTo(40, 0.1));
47+
}
48+
49+
@Test
50+
public void integerHundred() throws Exception {
51+
assertThat("integer 100 percentile", ddpInteger.getPercentile(100), closeTo(50, 0.1));
52+
}
53+
54+
@Test
55+
public void zero() {
56+
assertThat("10 percentile", ddp.getPercentile(10), closeTo(1.5, 0.001));
57+
}
58+
59+
@Test
60+
public void median() {
61+
assertThat("median", ddp.getPercentile(50), closeTo(3.5, 0.001));
62+
}
63+
64+
@Test
65+
public void seventyFife() {
66+
assertThat("75 percentile", ddp.getPercentile(75), closeTo(4.1, 0.001));
67+
}
68+
69+
@Test
70+
public void hundred() {
71+
assertThat("100 percentile", ddp.getPercentile(100), closeTo(5.2, 0.001));
72+
}
73+
}

0 commit comments

Comments
 (0)