1 package io.jawk.backend;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import java.io.PrintStream;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import io.jawk.intermediate.Opcode;
33
34
35
36
37
38 public final class ProfilingReport {
39
40 private final List<Entry> tupleEntries;
41 private final List<Entry> functionEntries;
42
43
44
45
46
47
48 public static ProfilingReport empty() {
49 return new ProfilingReport(
50 Collections.<Opcode, Accumulator>emptyMap(),
51 Collections.<String, Accumulator>emptyMap());
52 }
53
54 ProfilingReport(Map<Opcode, Accumulator> tupleStats, Map<String, Accumulator> functionStats) {
55 this.tupleEntries = Collections.unmodifiableList(toTupleEntries(tupleStats));
56 this.functionEntries = Collections.unmodifiableList(toFunctionEntries(functionStats));
57 }
58
59
60
61
62
63
64 public List<Entry> getTupleEntries() {
65 return tupleEntries;
66 }
67
68
69
70
71
72
73 public List<Entry> getFunctionEntries() {
74 return functionEntries;
75 }
76
77
78
79
80
81
82 public void print(PrintStream out) {
83 out.println("Jawk profiling report");
84 out.println();
85 printSection(out, "Tuple execution", tupleEntries);
86 out.println();
87 printSection(out, "Function execution", functionEntries);
88 }
89
90 private static void printSection(PrintStream out, String title, List<Entry> entries) {
91 out.println(title + ":");
92 if (entries.isEmpty()) {
93 out.println(" (none)");
94 return;
95 }
96 out.printf(Locale.ROOT, " %-32s %12s %14s %14s%n", "Name", "Count", "Time (ms)", "Avg (ns)");
97 for (Entry entry : entries) {
98 out
99 .printf(
100 Locale.ROOT,
101 " %-32s %12d %14.3f %14.0f%n",
102 entry.getName(),
103 entry.getCount(),
104 entry.getTotalNanos() / 1_000_000.0d,
105 entry.getAverageNanos());
106 }
107 }
108
109 private static List<Entry> toTupleEntries(Map<Opcode, Accumulator> stats) {
110 List<Entry> entries = new ArrayList<Entry>(stats.size());
111 for (Map.Entry<Opcode, Accumulator> entry : stats.entrySet()) {
112 entries.add(new Entry(entry.getKey().name(), entry.getValue().count, entry.getValue().totalNanos));
113 }
114 sort(entries);
115 return entries;
116 }
117
118 private static List<Entry> toFunctionEntries(Map<String, Accumulator> stats) {
119 List<Entry> entries = new ArrayList<Entry>(stats.size());
120 for (Map.Entry<String, Accumulator> entry : stats.entrySet()) {
121 entries.add(new Entry(entry.getKey(), entry.getValue().count, entry.getValue().totalNanos));
122 }
123 sort(entries);
124 return entries;
125 }
126
127 private static void sort(List<Entry> entries) {
128 Collections
129 .sort(
130 entries,
131 Comparator
132 .comparingLong(Entry::getTotalNanos)
133 .reversed()
134 .thenComparing(Comparator.comparingLong(Entry::getCount).reversed())
135 .thenComparing(Entry::getName));
136 }
137
138 static final class Accumulator {
139 private long count;
140 private long totalNanos;
141
142 void add(long elapsedNanos) {
143 count++;
144 totalNanos += elapsedNanos;
145 }
146 }
147
148
149
150
151 public static final class Entry {
152 private final String name;
153 private final long count;
154 private final long totalNanos;
155
156 private Entry(String name, long count, long totalNanos) {
157 this.name = name;
158 this.count = count;
159 this.totalNanos = totalNanos;
160 }
161
162
163
164
165
166
167 public String getName() {
168 return name;
169 }
170
171
172
173
174
175
176 public long getCount() {
177 return count;
178 }
179
180
181
182
183
184
185 public long getTotalNanos() {
186 return totalNanos;
187 }
188
189
190
191
192
193
194 public double getAverageNanos() {
195 return count == 0 ? 0.0d : (double) totalNanos / count;
196 }
197 }
198 }