1
2
3
4
5
6 package dev.metaschema.core.model.constraint;
7
8 import java.net.URI;
9 import java.time.Instant;
10 import java.util.ArrayDeque;
11 import java.util.Collections;
12 import java.util.Deque;
13 import java.util.Map;
14 import java.util.concurrent.ConcurrentHashMap;
15
16 import dev.metaschema.core.metapath.item.node.INodeItem;
17 import edu.umd.cs.findbugs.annotations.NonNull;
18 import edu.umd.cs.findbugs.annotations.Nullable;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 public class TimingCollector implements ValidationEventListener {
42 @NonNull
43 private final ConcurrentHashMap<ValidationPhase, TimingRecord> phaseTimings = new ConcurrentHashMap<>();
44 @NonNull
45 private final ConcurrentHashMap<String, TimingRecord> constraintTimings = new ConcurrentHashMap<>();
46 @NonNull
47 private final ConcurrentHashMap<ILet, TimingRecord> letTimings = new ConcurrentHashMap<>();
48 @SuppressWarnings("PMD.AvoidUsingVolatile")
49 @Nullable
50 private volatile TimingRecord validationTiming;
51
52
53
54
55
56 @NonNull
57 private final ThreadLocal<Deque<Long>> startTimeStack = ThreadLocal.withInitial(ArrayDeque::new);
58
59
60
61
62 public TimingCollector() {
63
64 }
65
66
67
68
69 private void pushStartTime() {
70 startTimeStack.get().push(System.nanoTime());
71 }
72
73
74
75
76
77
78 private long popElapsedNs() {
79 Long startNs = startTimeStack.get().poll();
80 if (startNs == null) {
81 return 0L;
82 }
83 return System.nanoTime() - startNs;
84 }
85
86 @Override
87 public void beforeValidation(@NonNull URI document) {
88 TimingRecord record = validationTiming;
89 if (record == null) {
90 record = new TimingRecord();
91 validationTiming = record;
92 }
93 record.recordStart(Instant.now());
94 pushStartTime();
95 }
96
97 @Override
98 public void afterValidation(@NonNull URI document) {
99 long elapsed = popElapsedNs();
100 TimingRecord record = validationTiming;
101 if (record != null) {
102 record.recordEnd(elapsed, Instant.now());
103 }
104 }
105
106 @Override
107 public void beforePhase(@NonNull ValidationPhase phase) {
108 phaseTimings.computeIfAbsent(phase, k -> new TimingRecord())
109 .recordStart(Instant.now());
110 pushStartTime();
111 }
112
113 @Override
114 public void afterPhase(@NonNull ValidationPhase phase) {
115 long elapsed = popElapsedNs();
116 TimingRecord record = phaseTimings.get(phase);
117 if (record != null) {
118 record.recordEnd(elapsed, Instant.now());
119 }
120 }
121
122 @Override
123 public void beforeConstraintEvaluation(@NonNull IConstraint constraint, @NonNull INodeItem target) {
124 String id = constraint.getInternalIdentifier();
125 constraintTimings.computeIfAbsent(id, k -> new TimingRecord())
126 .recordStart(Instant.now());
127 pushStartTime();
128 }
129
130 @Override
131 public void afterConstraintEvaluation(@NonNull IConstraint constraint, @NonNull INodeItem target) {
132 long elapsed = popElapsedNs();
133 String id = constraint.getInternalIdentifier();
134 TimingRecord record = constraintTimings.get(id);
135 if (record != null) {
136 record.recordEnd(elapsed, Instant.now());
137 }
138 }
139
140 @Override
141 public void beforeLetEvaluation(@NonNull ILet let) {
142 letTimings.computeIfAbsent(let, k -> new TimingRecord())
143 .recordStart(Instant.now());
144 pushStartTime();
145 }
146
147 @Override
148 public void afterLetEvaluation(@NonNull ILet let) {
149 long elapsed = popElapsedNs();
150 TimingRecord record = letTimings.get(let);
151 if (record != null) {
152 record.recordEnd(elapsed, Instant.now());
153 }
154 }
155
156
157
158
159
160
161
162
163 @Nullable
164 public TimingRecord getPhaseTiming(@NonNull ValidationPhase phase) {
165 return phaseTimings.get(phase);
166 }
167
168
169
170
171
172
173 @NonNull
174 public Map<ValidationPhase, TimingRecord> getPhaseTimings() {
175 return Collections.unmodifiableMap(phaseTimings);
176 }
177
178
179
180
181
182
183
184
185 @Nullable
186 public TimingRecord getConstraintTiming(@NonNull String constraintId) {
187 return constraintTimings.get(constraintId);
188 }
189
190
191
192
193
194
195 @NonNull
196 public Map<String, TimingRecord> getConstraintTimings() {
197 return Collections.unmodifiableMap(constraintTimings);
198 }
199
200
201
202
203
204
205
206
207 @Nullable
208 public TimingRecord getLetTiming(@NonNull ILet let) {
209 return letTimings.get(let);
210 }
211
212
213
214
215
216
217 @NonNull
218 public Map<ILet, TimingRecord> getLetTimings() {
219 return Collections.unmodifiableMap(letTimings);
220 }
221
222
223
224
225
226
227
228 @Nullable
229 public TimingRecord getValidationTiming() {
230 return validationTiming;
231 }
232 }