• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2020 Network New Technologies Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.networknt.schema;
17 
18 import com.fasterxml.jackson.databind.JsonNode;
19 import com.fasterxml.jackson.databind.node.*;
20 import org.junit.jupiter.api.BeforeEach;
21 import org.junit.jupiter.api.Disabled;
22 import org.junit.jupiter.api.Test;
23 
24 import java.math.BigDecimal;
25 import java.math.BigInteger;
26 
27 import static java.lang.String.format;
28 import static java.lang.System.out;
29 
30 @Disabled
31 public class ThresholdMixinPerfTest {
32     private static long thresholdIntegral = Long.MAX_VALUE - 1;
33 
34 
35     private final LongNode maximumLong = new LongNode(thresholdIntegral);
36     private final BigIntegerNode maximumBigInt = new BigIntegerNode(BigInteger.valueOf(thresholdIntegral));
37 
38     private final LongNode valueLong = new LongNode(Long.MAX_VALUE);
39     private final BigIntegerNode valueBigInt = new BigIntegerNode(BigInteger.valueOf(Long.MAX_VALUE));
40 
41     //    private final double threshold = Double.MAX_VALUE - 1;
42     private final double threshold = 1797693.134E+5D;
43     private final DoubleNode maximumDouble = new DoubleNode(threshold);
44     private final DecimalNode maximumDecimal = new DecimalNode(BigDecimal.valueOf(threshold));
45 
46     private final double value = threshold + 1;
47     private final DoubleNode valueDouble = new DoubleNode(value);
48     private final DecimalNode valueDecimal = new DecimalNode(new BigDecimal(value));
49     private final TextNode valueTextual = new TextNode(String.valueOf(value));
50 
51     private final String maximumText = maximumDouble.asText();
52     private final BigDecimal max = new BigDecimal(maximumText);
53 
54     private final int executeTimes = 200000;
55     private final boolean excludeEqual = false;
56 
57     double baseTimeForDouble;
58     private double baseTimeForLong;
59 
60     @BeforeEach
baseTimeEstimate()61     public void baseTimeEstimate() {
62         baseTimeForDouble = getAvgTimeViaMixin(asDouble, valueDouble, executeTimes);
63         out.println(format("Base execution time (comparing two DoubleNodes) %f ns", baseTimeForDouble));
64 
65         baseTimeForLong = getAvgTimeViaMixin(asLong, valueLong, executeTimes);
66         out.println(format("Base execution time (comparing two LongeNodes) %f ns \n", baseTimeForDouble));
67     }
68 
69     @Test
currentTimeEstimate()70     public void currentTimeEstimate() {
71         out.println("Estimating time for current implementation:");
72         double currentAvgTimeOnDouble = getAvgTimeViaMixin(currentImplementationDouble, valueDouble, executeTimes);
73         out.println(format("Current double on double execution time %f ns, %f times slower", currentAvgTimeOnDouble, (currentAvgTimeOnDouble / baseTimeForDouble)));
74 
75         double currentAvgTimeOnDecimal = getAvgTimeViaMixin(currentImplementationDouble, valueDecimal, executeTimes);
76         out.println(format("Current double on decimal execution time %f ns, %f times slower", currentAvgTimeOnDecimal, (currentAvgTimeOnDecimal / baseTimeForDouble)));
77 
78         double currentAvgTimeOnText = getAvgTimeViaMixin(currentImplementationDouble, valueTextual, executeTimes);
79         out.println(format("Current double on text execution time %f ns, %f times slower", currentAvgTimeOnText, (currentAvgTimeOnText / baseTimeForDouble)));
80 
81         double currentAvgTimeDecimalOnDouble = getAvgTimeViaMixin(currentImplementationDecimal, valueDouble, executeTimes);
82         out.println(format("Current decimal on double execution time %f ns, %f times slower", currentAvgTimeDecimalOnDouble, (currentAvgTimeDecimalOnDouble / baseTimeForDouble)));
83 
84         double currentAvgTimeDecimalOnDecimal = getAvgTimeViaMixin(currentImplementationDecimal, valueDecimal, executeTimes);
85         out.println(format("Current decimal on decimal execution time %f ns, %f times slower", currentAvgTimeDecimalOnDecimal, (currentAvgTimeDecimalOnDecimal / baseTimeForDouble)));
86 
87         double currentAvgTimeDecimalOnText = getAvgTimeViaMixin(currentImplementationDecimal, valueTextual, executeTimes);
88         out.println(format("Current decimal on text execution time %f ns, %f times slower", currentAvgTimeDecimalOnText, (currentAvgTimeDecimalOnText / baseTimeForDouble)));
89 
90         out.println(format("Cumulative average: %f\n\n", (currentAvgTimeOnDouble + currentAvgTimeOnDecimal + currentAvgTimeOnText + currentAvgTimeDecimalOnDouble + currentAvgTimeDecimalOnDecimal + currentAvgTimeDecimalOnText) / 6.0d));
91     }
92 
93     @Test
allInOneAproachTimeEstimate()94     public void allInOneAproachTimeEstimate() {
95         out.println("Estimating time threshold value agnostic mixin (aka allInOne):");
96         double allInOneDoubleOnDouble = getAvgTimeViaMixin(allInOneDouble, valueDouble, executeTimes);
97         out.println(format("AllInOne double on double execution time  %f ns, %f times slower", allInOneDoubleOnDouble, (allInOneDoubleOnDouble / baseTimeForDouble)));
98 
99         double allInOneDoubleOnDecimal = getAvgTimeViaMixin(allInOneDouble, valueDecimal, executeTimes);
100         out.println(format("AllInOne double on decimal execution time %f ns, %f times slower", allInOneDoubleOnDecimal, (allInOneDoubleOnDecimal / baseTimeForDouble)));
101 
102         double allInOneDoubleOnText = getAvgTimeViaMixin(allInOneDouble, valueTextual, executeTimes);
103         out.println(format("AllInOne double on text execution time %f ns, %f times slower", allInOneDoubleOnText, (allInOneDoubleOnText / baseTimeForDouble)));
104 
105         double allInOneDecimalOnDouble = getAvgTimeViaMixin(allInOneDecimal, valueDouble, executeTimes);
106         out.println(format("AllInOne decimal on double execution time %f ns, %f times slower", allInOneDecimalOnDouble, (allInOneDecimalOnDouble / baseTimeForDouble)));
107 
108         double allInOneDecimalOnDecimal = getAvgTimeViaMixin(allInOneDecimal, valueDecimal, executeTimes);
109         out.println(format("AllInOne decimal on decimal execution time %f ns, %f times slower", allInOneDecimalOnDecimal, (allInOneDecimalOnDecimal / baseTimeForDouble)));
110 
111         double allInOneDecimalOnText = getAvgTimeViaMixin(allInOneDecimal, valueTextual, executeTimes);
112         out.println(format("AllInOne decimal on text execution time %f ns, %f times slower", allInOneDecimalOnText, (allInOneDecimalOnText / baseTimeForDouble)));
113 
114         out.println(format("Cumulative average: %f\n\n", (allInOneDoubleOnDouble + allInOneDoubleOnDecimal + allInOneDoubleOnText + allInOneDecimalOnDouble + allInOneDecimalOnDecimal + allInOneDecimalOnText) / 6.0d));
115     }
116 
117     @Test
specificCaseForEachThresholdValue()118     public void specificCaseForEachThresholdValue() {
119         out.println("Estimating time for specific cases:");
120         double doubleValueAvgTime = getAvgTimeViaMixin(typedThreshold, valueDouble, executeTimes);
121         out.println(format("Typed threshold execution time %f ns, %f times slower", doubleValueAvgTime, (doubleValueAvgTime / baseTimeForDouble)));
122 
123         double decimalValueAvgTime = getAvgTimeViaMixin(typedThreshold, valueDecimal, executeTimes);
124         out.println(format("Typed threshold execution time %f ns, %f times slower", decimalValueAvgTime, (decimalValueAvgTime / baseTimeForDouble)));
125 
126         double textValueAvgTime = getAvgTimeViaMixin(typedThreshold, valueTextual, executeTimes);
127         out.println(format("Typed threshold execution time %f ns, %f times slower", textValueAvgTime, (textValueAvgTime / baseTimeForDouble)));
128 
129         out.println(format("Cumulative average: %f\n\n", (doubleValueAvgTime + decimalValueAvgTime + textValueAvgTime) / 3.0d));
130     }
131 
132     @Test
noMixinsFloatingTimeEstimate()133     public void noMixinsFloatingTimeEstimate() {
134         out.println("Estimating time no mixins at all (floating point values):");
135         double doubleValueAvgTime = getAvgTimeViaMixin(oneMixinForIntegerAndNumber, valueDecimal, executeTimes);
136         out.println(format("No mixins with double value time %f ns, %f times slower", doubleValueAvgTime, (doubleValueAvgTime / baseTimeForDouble)));
137 
138         double decimalValueAvgTime = getAvgTimeViaMixin(oneMixinForIntegerAndNumber, valueDecimal, executeTimes);
139         out.println(format("No mixins with decimal value time %f ns, %f times slower", decimalValueAvgTime, (decimalValueAvgTime / baseTimeForDouble)));
140 
141         double textValueAvgTime = getAvgTimeViaMixin(oneMixinForIntegerAndNumber, valueTextual, executeTimes);
142         out.println(format("No mixins with text value time %f ns, %f times slower", textValueAvgTime, (textValueAvgTime / baseTimeForDouble)));
143         out.println(format("Cumulative average: %f\n\n",
144                 (doubleValueAvgTime + decimalValueAvgTime + textValueAvgTime) / 3.0d));
145     }
146 
147     @Test
noMixinsIntegralTimeEstimate()148     public void noMixinsIntegralTimeEstimate() {
149         double longValueAvgTime = getAvgTimeViaMixin(oneMixinForIntegerAndNumber, new LongNode((long) value), executeTimes);
150         out.println(format("No mixins with long value time %f ns, %f times slower", longValueAvgTime, (longValueAvgTime / baseTimeForLong)));
151 
152         double bigIntValueAvgTime = getAvgTimeViaMixin(oneMixinForIntegerAndNumber, new BigIntegerNode(BigInteger.valueOf((long) value)), executeTimes);
153         out.println(format("No mixins with big int value time %f ns, %f times slower", bigIntValueAvgTime, (bigIntValueAvgTime / baseTimeForLong)));
154 
155         double textIntValueAvgTime = getAvgTimeViaMixin(oneMixinForIntegerAndNumber, new TextNode(String.valueOf((long) value)), executeTimes);
156         out.println(format("No mixins with text value time %f ns, %f times slower", textIntValueAvgTime, (textIntValueAvgTime / baseTimeForLong)));
157         out.println(format("Cumulative average: %f\n\n",
158                 (longValueAvgTime + bigIntValueAvgTime + textIntValueAvgTime) / 3.0d));
159     }
160 
161     ThresholdMixin allInOneDouble = new AllInOneThreshold(maximumDouble, false);
162     ThresholdMixin allInOneDecimal = new AllInOneThreshold(maximumDecimal, false);
163 
164     public static class AllInOneThreshold implements ThresholdMixin {
165 
166         private final BigDecimal bigDecimalMax;
167         JsonNode maximum;
168         private boolean excludeEqual;
169 
AllInOneThreshold(JsonNode maximum, boolean exludeEqual)170         AllInOneThreshold(JsonNode maximum, boolean exludeEqual) {
171             this.maximum = maximum;
172             this.excludeEqual = exludeEqual;
173             this.bigDecimalMax = new BigDecimal(maximum.asText());
174         }
175 
176         @Override
crossesThreshold(JsonNode node)177         public boolean crossesThreshold(JsonNode node) {
178             if (maximum.isDouble() && maximum.doubleValue() == Double.POSITIVE_INFINITY) {
179                 return false;
180             }
181             if (maximum.isDouble() && maximum.doubleValue() == Double.NEGATIVE_INFINITY) {
182                 return true;
183             }
184             if (maximum.isDouble() && node.isDouble()) {
185                 double lm = maximum.doubleValue();
186                 double val = node.doubleValue();
187                 return lm < val || (excludeEqual && lm == val);
188             }
189 
190             if (maximum.isFloatingPointNumber() && node.isFloatingPointNumber()) {
191                 BigDecimal value = node.decimalValue();
192                 int compare = value.compareTo(bigDecimalMax);
193                 return compare > 0 || (excludeEqual && compare == 0);
194             }
195 
196             BigDecimal value = new BigDecimal(node.asText());
197             int compare = value.compareTo(bigDecimalMax);
198             return compare > 0 || (excludeEqual && compare == 0);
199         }
200 
201         @Override
thresholdValue()202         public String thresholdValue() {
203             return maximum.asText();
204         }
205     }
206 
207     ;
208 
209     ThresholdMixin asDouble = new ThresholdMixin() {
210         @Override
211         public boolean crossesThreshold(JsonNode node) {
212             double lm = maximumDouble.doubleValue();
213             double val = node.doubleValue();
214             return lm < val || (excludeEqual && lm == val);
215         }
216 
217         @Override
218         public String thresholdValue() {
219             return maximumText;
220         }
221     };
222 
223     ThresholdMixin asLong = new ThresholdMixin() {
224         @Override
225         public boolean crossesThreshold(JsonNode node) {
226             long lm = maximumLong.longValue();
227             long val = node.longValue();
228             return lm < val || (excludeEqual && lm == val);
229         }
230 
231         @Override
232         public String thresholdValue() {
233             return maximumText;
234         }
235     };
236 
237     ThresholdMixin typedThreshold = new ThresholdMixin() {
238         @Override
239         public boolean crossesThreshold(JsonNode node) {
240             if (node.isDouble()) {
241                 double lm = maximumDouble.doubleValue();
242                 double val = node.doubleValue();
243                 return lm < val || (excludeEqual && lm == val);
244             }
245 
246             if (node.isBigDecimal()) {
247                 BigDecimal value = node.decimalValue();
248                 int compare = value.compareTo(max);
249                 return compare > 0 || (excludeEqual && compare == 0);
250             }
251 
252             BigDecimal value = new BigDecimal(node.asText());
253             int compare = value.compareTo(max);
254             return compare > 0 || (excludeEqual && compare == 0);
255         }
256 
257         @Override
258         public String thresholdValue() {
259             return maximumText;
260         }
261     };
262 
263     ThresholdMixin currentImplementationDouble = new ThresholdMixin() {
264         @Override
265         public boolean crossesThreshold(JsonNode node) {
266             if (maximumDouble.isDouble() && maximumDouble.doubleValue() == Double.POSITIVE_INFINITY) {
267                 return false;
268             }
269             if (maximumDouble.isDouble() && maximumDouble.doubleValue() == Double.NEGATIVE_INFINITY) {
270                 return true;
271             }
272             if (node.isDouble() && node.doubleValue() == Double.NEGATIVE_INFINITY) {
273                 return false;
274             }
275             if (node.isDouble() && node.doubleValue() == Double.POSITIVE_INFINITY) {
276                 return true;
277             }
278             final BigDecimal max = new BigDecimal(maximumText);
279             BigDecimal value = new BigDecimal(node.asText());
280             int compare = value.compareTo(max);
281             return compare > 0 || (excludeEqual && compare == 0);
282         }
283 
284         @Override
285         public String thresholdValue() {
286             return maximumText;
287         }
288     };
289 
290 
291     ThresholdMixin currentImplementationDecimal = new ThresholdMixin() {
292         @Override
293         public boolean crossesThreshold(JsonNode node) {
294             if (maximumDecimal.isDouble() && maximumDecimal.doubleValue() == Double.POSITIVE_INFINITY) {
295                 return false;
296             }
297             if (maximumDecimal.isDouble() && maximumDecimal.doubleValue() == Double.NEGATIVE_INFINITY) {
298                 return true;
299             }
300             if (node.isDouble() && node.doubleValue() == Double.NEGATIVE_INFINITY) {
301                 return false;
302             }
303             if (node.isDouble() && node.doubleValue() == Double.POSITIVE_INFINITY) {
304                 return true;
305             }
306             final BigDecimal max = new BigDecimal(maximumText);
307             BigDecimal value = new BigDecimal(node.asText());
308             int compare = value.compareTo(max);
309             return compare > 0 || (excludeEqual && compare == 0);
310         }
311 
312         @Override
313         public String thresholdValue() {
314             return maximumText;
315         }
316     };
317 
318     ThresholdMixin oneMixinForIntegerAndNumber = new ThresholdMixin() {
319         @Override
320         public boolean crossesThreshold(JsonNode node) {
321             BigDecimal value = new BigDecimal(node.asText());
322             int compare = value.compareTo(max);
323             return compare > 0 || (excludeEqual && compare == 0);
324         }
325 
326         @Override
327         public String thresholdValue() {
328             return null;
329         }
330     };
331 
getAvgTimeViaMixin(ThresholdMixin mixin, JsonNode value, int iterations)332     private double getAvgTimeViaMixin(ThresholdMixin mixin, JsonNode value, int iterations) {
333 //        boolean excludeEqual = false;
334         long totalTime = 0;
335         for (int i = 0; i < iterations; i++) {
336             long start = System.nanoTime();
337             try {
338                 mixin.crossesThreshold(value);
339             } finally {
340                 totalTime += System.nanoTime() - start;
341             }
342         }
343         return totalTime / (iterations * 1.0D);
344     }
345 }
346