1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.statistics.examples.jmh.descriptive;
19
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.EnumSet;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Objects;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.atomic.AtomicInteger;
28 import java.util.function.BiFunction;
29 import java.util.function.BinaryOperator;
30 import java.util.logging.Logger;
31 import org.apache.commons.rng.UniformRandomProvider;
32 import org.apache.commons.rng.sampling.ArraySampler;
33 import org.apache.commons.rng.sampling.PermutationSampler;
34 import org.apache.commons.rng.sampling.distribution.DiscreteUniformSampler;
35 import org.apache.commons.rng.sampling.distribution.SharedStateDiscreteSampler;
36 import org.apache.commons.rng.simple.RandomSource;
37 import org.apache.commons.statistics.descriptive.Quantile;
38 import org.openjdk.jmh.annotations.Benchmark;
39 import org.openjdk.jmh.annotations.BenchmarkMode;
40 import org.openjdk.jmh.annotations.Fork;
41 import org.openjdk.jmh.annotations.Level;
42 import org.openjdk.jmh.annotations.Measurement;
43 import org.openjdk.jmh.annotations.Mode;
44 import org.openjdk.jmh.annotations.OutputTimeUnit;
45 import org.openjdk.jmh.annotations.Param;
46 import org.openjdk.jmh.annotations.Scope;
47 import org.openjdk.jmh.annotations.Setup;
48 import org.openjdk.jmh.annotations.State;
49 import org.openjdk.jmh.annotations.Warmup;
50 import org.openjdk.jmh.infra.Blackhole;
51
52
53
54
55 @BenchmarkMode(Mode.AverageTime)
56 @OutputTimeUnit(TimeUnit.NANOSECONDS)
57 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
58 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
59 @State(Scope.Benchmark)
60 @Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx8192M"})
61 public class QuantilePerformance {
62
63 private static final String JDK = "JDK";
64
65 private static final String CM3 = "CM3";
66
67 private static final String CM4 = "CM4";
68
69 private static final String STATISTICS = "Statistics";
70
71
72 private static final RandomSource RANDOM_SOURCE = RandomSource.XO_RO_SHI_RO_128_PP;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 @State(Scope.Benchmark)
102 public abstract static class AbstractDataSource {
103
104 private static final String ALL = "all";
105
106 private static final String BM = "bm";
107
108
109 private static final String VALOIS = "valois";
110
111
112
113 private static final AtomicInteger LOG_SIZE = new AtomicInteger();
114
115
116
117
118 enum Distribution {
119
120
121
122 SAWTOOTH,
123
124 RANDOM,
125
126 STAGGER,
127
128 PLATEAU,
129
130 SHUFFLE,
131
132
133
134
135
136
137
138
139
140 SHARKTOOTH,
141
142
143
144
145 SORTED,
146
147 ONEZERO,
148
149 M3KILLER,
150
151 ROTATED,
152
153 TWOFACED,
154
155 ORGANPIPE;
156 }
157
158
159
160
161 enum Modification {
162
163 COPY,
164
165 REVERSE,
166
167 REVERSE_FRONT,
168
169 REVERSE_BACK,
170
171 SORT,
172
173
174
175
176
177
178
179
180
181 DESCENDING,
182
183 DITHER;
184 }
185
186
187
188
189 protected int[] order;
190
191 protected UniformRandomProvider rng;
192
193
194
195 @Param({BM})
196 private String distribution = BM;
197
198
199
200 @Param({BM})
201 private String modification = BM;
202
203
204
205 @Param({"1"})
206 private int range = 1;
207
208
209
210 @Param({"0"})
211 private int seed;
212
213
214
215 @Param({"0"})
216 private int offset;
217
218
219
220 @Param({"0"})
221 private int samples;
222
223
224
225
226
227
228 @Param({"-7450238124206088695"})
229 private long rngSeed = -7450238124206088695L;
230
231
232
233
234
235
236 private int[][] data;
237
238
239
240
241
242
243
244
245
246 public double[] getData(int index) {
247 return getDataSample(order[index]);
248 }
249
250
251
252
253
254
255
256
257
258 public int[] getIntData(int index) {
259 return getIntDataSample(order[index]);
260 }
261
262
263
264
265
266
267
268 protected double[] getDataSample(int index) {
269 final int[] a = data[index];
270 final double[] x = new double[a.length];
271 for (int i = -1; ++i < a.length;) {
272 x[i] = a[i];
273 }
274 return x;
275 }
276
277
278
279
280
281
282
283 protected int[] getIntDataSample(int index) {
284
285 final int[] a = data[index];
286 final int[] x = new int[a.length];
287 for (int i = -1; ++i < a.length;) {
288 x[i] = a[i];
289 }
290 return x;
291 }
292
293
294
295
296
297
298
299 public int getDataSize(int index) {
300 return data[index].length;
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 public int size() {
318 return data.length;
319 }
320
321
322
323
324 @Setup(Level.Iteration)
325 public void setup() {
326 Objects.requireNonNull(distribution);
327 Objects.requireNonNull(modification);
328
329
330 final EnumSet<Distribution> dist = getDistributions();
331 final int length = getLength();
332 if (length < 1) {
333 throw new IllegalStateException("Unsupported length: " + length);
334 }
335
336
337
338
339 final int r = range > 0 ? range : 0;
340 if (length + (long) r > Integer.MAX_VALUE) {
341 throw new IllegalStateException("Unsupported upper length: " + length);
342 }
343 final int length2 = length + r;
344
345
346 if (rngSeed == 0) {
347 rngSeed = RandomSource.createLong();
348 }
349 if (rng == null) {
350
351 rng = RANDOM_SOURCE.create(rngSeed);
352 }
353
354
355 if (dist.contains(Distribution.RANDOM) && dist.size() == 1 && samples > 0) {
356 data = new int[samples][];
357 final int upper = seed > 0 ? seed : Integer.MAX_VALUE;
358 final SharedStateDiscreteSampler s1 = DiscreteUniformSampler.of(rng, 0, upper);
359 final SharedStateDiscreteSampler s2 = DiscreteUniformSampler.of(rng, length, length2);
360 for (int i = 0; i < data.length; i++) {
361 final int[] a = new int[s2.sample()];
362 for (int j = a.length; --j >= 0;) {
363 a[j] = s1.sample();
364 }
365 data[i] = a;
366 }
367 return;
368 }
369
370
371 data = null;
372 final int o = offset;
373 offset = rng.nextInt();
374
375 final EnumSet<Modification> mod = getModifications();
376
377
378
379
380 final ArrayList<int[]> sampleData = new ArrayList<>();
381 for (int n = length; n <= length2; n++) {
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396 for (final int m : createSeeds(seed, n)) {
397 final List<int[]> d = createDistributions(dist, rng, n, m, o);
398 for (int i = 0; i < d.size(); i++) {
399 final int[] x = d.get(i);
400 if (mod.contains(Modification.COPY)) {
401
402
403 sampleData.add(x);
404 }
405 if (mod.contains(Modification.REVERSE)) {
406 sampleData.add(reverse(x, 0, n));
407 }
408 if (mod.contains(Modification.REVERSE_FRONT)) {
409 sampleData.add(reverse(x, 0, n >>> 1));
410 }
411 if (mod.contains(Modification.REVERSE_BACK)) {
412 sampleData.add(reverse(x, n >>> 1, n));
413 }
414
415 if (mod.contains(Modification.SORT) ||
416 mod.contains(Modification.DESCENDING)) {
417 final int[] y = x.clone();
418 Arrays.sort(y);
419 if (mod.contains(Modification.DESCENDING)) {
420 sampleData.add(reverse(y, 0, n));
421 }
422 if (mod.contains(Modification.SORT)) {
423 sampleData.add(y);
424 }
425 }
426 if (mod.contains(Modification.DITHER)) {
427 sampleData.add(dither(x));
428 }
429 }
430 }
431 }
432 data = sampleData.toArray(int[][]::new);
433 if (LOG_SIZE.getAndSet(length) != length) {
434 Logger.getLogger(getClass().getName()).info(
435 () -> String.format("Data length: [%d, %d] n=%d", length, length2, data.length));
436 }
437 }
438
439
440
441
442
443
444
445
446 @Setup(Level.Invocation)
447 public void createOrder() {
448 if (order == null) {
449
450 order = PermutationSampler.natural(size());
451 }
452 ArraySampler.shuffle(rng, order);
453 }
454
455
456
457
458 private EnumSet<Distribution> getDistributions() {
459 EnumSet<Distribution> dist;
460 if (BM.equals(distribution)) {
461 dist = EnumSet.of(
462 Distribution.SAWTOOTH,
463 Distribution.RANDOM,
464 Distribution.STAGGER,
465 Distribution.PLATEAU,
466 Distribution.SHUFFLE);
467 } else if (VALOIS.equals(distribution)) {
468 dist = EnumSet.of(
469 Distribution.RANDOM,
470 Distribution.SORTED,
471 Distribution.ONEZERO,
472 Distribution.M3KILLER,
473 Distribution.ROTATED,
474 Distribution.TWOFACED,
475 Distribution.ORGANPIPE);
476 } else {
477 dist = getEnumFromParam(Distribution.class, distribution);
478 }
479 return dist;
480 }
481
482
483
484
485 private EnumSet<Modification> getModifications() {
486 EnumSet<Modification> mod;
487 if (BM.equals(modification)) {
488
489 mod = EnumSet.allOf(Modification.class);
490
491 mod.remove(Modification.DESCENDING);
492 } else if (VALOIS.equals(modification)) {
493
494 mod = EnumSet.of(Modification.COPY);
495 } else {
496 mod = getEnumFromParam(Modification.class, modification);
497 }
498 return mod;
499 }
500
501
502
503
504
505
506
507
508
509 static <E extends Enum<E>> EnumSet<E> getEnumFromParam(Class<E> cls, String parameters) {
510 if (ALL.equals(parameters)) {
511 return EnumSet.allOf(cls);
512 }
513 final EnumSet<E> set = EnumSet.noneOf(cls);
514 final String s = parameters.toUpperCase(Locale.ROOT);
515 for (final E e : cls.getEnumConstants()) {
516
517 for (int i = s.indexOf(e.name(), 0); i >= 0; i = s.indexOf(e.name(), i)) {
518
519
520 i += e.name().length();
521 if (i == s.length() || s.charAt(i) == ':') {
522 set.add(e);
523 break;
524 }
525 }
526 }
527 if (set.isEmpty()) {
528 throw new IllegalStateException("Unknown parameters: " + parameters);
529 }
530 return set;
531 }
532
533
534
535
536
537
538
539
540
541
542
543
544 private static int[] createSeeds(int seed, int n) {
545
546 if (seed - 1 >= 0) {
547 return new int[] {seed};
548 }
549
550
551
552
553
554
555 int c = 33 - Integer.numberOfLeadingZeros(n - 1);
556 final int[] seeds = new int[c];
557 c = 0;
558 for (int m = 1; c != seeds.length; m *= 2) {
559 seeds[c++] = m;
560 }
561 return seeds;
562 }
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583 private static List<int[]> createDistributions(EnumSet<Distribution> dist,
584 UniformRandomProvider rng, int n, int m, int o) {
585 final ArrayList<int[]> distData = new ArrayList<>(6);
586 int[] x;
587
588 if (dist.contains(Distribution.SAWTOOTH) && m != 1) {
589 distData.add(x = new int[n]);
590
591
592
593 final int mask = m - 1;
594 if ((m & mask) == 0) {
595 for (int i = -1; ++i < n;) {
596 x[i] = (i + o) & mask;
597 }
598 } else {
599
600 int j = Integer.remainderUnsigned(o, m);
601 for (int i = -1; ++i < n;) {
602 j = j % m;
603 x[i] = j++;
604 }
605 }
606 }
607 if (dist.contains(Distribution.RANDOM) && m != 1) {
608 distData.add(x = new int[n]);
609
610
611 final SharedStateDiscreteSampler s = DiscreteUniformSampler.of(rng, 0, m - 1);
612 for (int i = -1; ++i < n;) {
613 x[i] = s.sample();
614 }
615 }
616 if (dist.contains(Distribution.STAGGER)) {
617 distData.add(x = new int[n]);
618
619 final long nn = n;
620 final long oo = Integer.toUnsignedLong(o);
621 for (int i = -1; ++i < n;) {
622 final long j = i + oo;
623 x[i] = (int) ((j * m + j) % nn);
624 }
625 }
626 if (dist.contains(Distribution.PLATEAU)) {
627 distData.add(x = new int[n]);
628
629 for (int i = Math.min(n, m); --i >= 0;) {
630 x[i] = i;
631 }
632 for (int i = m - 1; ++i < n;) {
633 x[i] = m;
634 }
635
636 final int n1 = Integer.remainderUnsigned(o, n);
637 if (n1 != 0) {
638 final int[] a = x.clone();
639 final int n2 = n - n1;
640 System.arraycopy(a, 0, x, n1, n2);
641 System.arraycopy(a, n2, x, 0, n1);
642 }
643 }
644 if (dist.contains(Distribution.SHUFFLE) && m != 1) {
645 distData.add(x = new int[n]);
646
647 final SharedStateDiscreteSampler s = DiscreteUniformSampler.of(rng, 0, m - 1);
648 for (int i = -1, j = 0, k = 1; ++i < n;) {
649 x[i] = s.sample() != 0 ? (j += 2) : (k += 2);
650 }
651 }
652
653 if (dist.contains(Distribution.SHARKTOOTH) && m != 1) {
654 distData.add(x = new int[n]);
655
656 int i = -1;
657 int j = (o & Integer.MAX_VALUE) % m - 1;
658 OUTER:
659 for (;;) {
660 while (++j < m) {
661 if (++i == n) {
662 break OUTER;
663 }
664 x[i] = j;
665 }
666 while (--j >= 0) {
667 if (++i == n) {
668 break OUTER;
669 }
670 x[i] = j;
671 }
672 }
673 }
674
675 if (dist.contains(Distribution.SORTED)) {
676 distData.add(x = new int[n]);
677 for (int i = -1; ++i < n;) {
678 x[i] = i;
679 }
680 }
681 if (dist.contains(Distribution.ONEZERO)) {
682 distData.add(x = new int[n]);
683
684
685
686
687 final int end = n & ~31;
688 for (int i = 0; i < end; i += 32) {
689 int z = rng.nextInt();
690 for (int j = -1; ++j < 32;) {
691 x[i + j] = z & 1;
692 z >>>= 1;
693 }
694 }
695 for (int i = end; ++i < n;) {
696 x[i] = rng.nextBoolean() ? 1 : 0;
697 }
698 }
699 if (dist.contains(Distribution.M3KILLER)) {
700 distData.add(x = new int[n]);
701 medianOf3Killer(x);
702 }
703 if (dist.contains(Distribution.ROTATED)) {
704 distData.add(x = new int[n]);
705
706
707 for (int i = 0; i < n;) {
708 x[i] = ++i;
709 }
710 x[n - 1] = 0;
711 }
712 if (dist.contains(Distribution.TWOFACED)) {
713 distData.add(x = new int[n]);
714
715
716 medianOf3Killer(x);
717 final int j = 4 * (31 - Integer.numberOfLeadingZeros(n));
718 final int n2 = n >>> 1;
719 ArraySampler.shuffle(rng, x, j, n2);
720 ArraySampler.shuffle(rng, x, n2 + j, n);
721 }
722 if (dist.contains(Distribution.ORGANPIPE)) {
723 distData.add(x = new int[n]);
724
725
726 for (int i = -1, j = n; ++i <= --j;) {
727 x[i] = i;
728 x[j] = i;
729 }
730 }
731 return distData;
732 }
733
734
735
736
737
738
739 private static void medianOf3Killer(int[] x) {
740
741
742
743
744
745
746
747
748
749 final int n = x.length;
750 final int k = n >>> 1;
751 for (int i = 0; i < k; i++) {
752 x[i] = ++i;
753 x[i] = k + i;
754 }
755 for (int i = k - 1, j = 2; ++i < n; j += 2) {
756 x[i] = j;
757 }
758 }
759
760
761
762
763
764
765
766
767
768 private static int[] reverse(int[] x, int from, int to) {
769 final int[] a = x.clone();
770 for (int i = from - 1, j = to; ++i < --j;) {
771 final int v = a[i];
772 a[i] = a[j];
773 a[j] = v;
774 }
775 return a;
776 }
777
778
779
780
781
782
783
784 private static int[] dither(int[] x) {
785 final int[] a = x.clone();
786 for (int i = a.length; --i >= 0;) {
787
788
789
790 a[i] += i % 5;
791 }
792 return a;
793 }
794
795
796
797
798
799
800
801 protected abstract int getLength();
802
803
804
805
806
807
808 final int getRange() {
809 return range;
810 }
811 }
812
813
814
815
816 @State(Scope.Benchmark)
817 public static class DataSource extends AbstractDataSource {
818
819 @Param({"1000", "100000"})
820 private int length;
821
822
823 @Override
824 protected int getLength() {
825 return length;
826 }
827 }
828
829
830
831
832 @State(Scope.Benchmark)
833 public static class QuantileSource {
834
835
836
837 @Param({"0.25:0.5:0.75",
838 "0.01:0.99",
839 "1e-100:1.0",
840 "0.25:0.75",
841 "0.001:0.005:0.01:0.02:0.05:0.1:0.5",
842 "0.01:0.05:0.1:0.5:0.9:0.95:0.99"})
843 private String quantiles;
844
845
846 private double[] data;
847
848
849
850
851 public double[] getData() {
852 return data;
853 }
854
855
856
857
858 @Setup
859 public void setup() {
860 data = Arrays.stream(quantiles.split(":")).mapToDouble(Double::parseDouble).toArray();
861 }
862 }
863
864
865
866
867 @State(Scope.Benchmark)
868 public static class QuantileRangeSource {
869
870 @Param({"0.01"})
871 private double lowerQ;
872
873 @Param({"0.99"})
874 private double upperQ;
875
876 @Param({"100"})
877 private int quantiles;
878
879
880 private double[] data;
881
882
883
884
885 public double[] getData() {
886 return data;
887 }
888
889
890
891
892 @Setup
893 public void setup() {
894 if (quantiles < 2) {
895 throw new IllegalStateException("Bad quantile count: " + quantiles);
896 }
897 if (!(lowerQ >= 0 && upperQ <= 1)) {
898 throw new IllegalStateException("Bad quantile range: [" + lowerQ + ", " + upperQ + "]");
899 }
900 data = new double[quantiles];
901 for (int i = 0; i < quantiles; i++) {
902
903 final double u = i / (quantiles - 1.0);
904 data[i] = (1 - u) * lowerQ + u * upperQ;
905 }
906 }
907 }
908
909
910
911
912 @State(Scope.Benchmark)
913 public static class DoubleQuantileFunctionSource {
914
915 @Param({JDK, CM3, CM4, STATISTICS})
916 private String name;
917
918
919 private BinaryOperator<double[]> function;
920
921
922
923
924 public BinaryOperator<double[]> getFunction() {
925 return function;
926 }
927
928
929
930
931 @Setup
932 public void setup() {
933
934
935 if (JDK.equals(name)) {
936 function = DoubleQuantileFunctionSource::sortQuantile;
937 } else if (CM3.equals(name)) {
938
939
940 final org.apache.commons.math3.stat.descriptive.rank.Percentile s =
941 new org.apache.commons.math3.stat.descriptive.rank.Percentile().withNaNStrategy(
942 org.apache.commons.math3.stat.ranking.NaNStrategy.FIXED);
943 function = (x, p) -> {
944 final double[] q = new double[p.length];
945 s.setData(x);
946 for (int i = 0; i < p.length; i++) {
947
948 q[i] = s.evaluate(p[i] * 100);
949 }
950 return q;
951 };
952 } else if (CM4.equals(name)) {
953
954
955 final org.apache.commons.math4.legacy.stat.descriptive.rank.Percentile s =
956 new org.apache.commons.math4.legacy.stat.descriptive.rank.Percentile().withNaNStrategy(
957 org.apache.commons.math4.legacy.stat.ranking.NaNStrategy.FIXED);
958 function = (x, p) -> {
959 final double[] q = new double[p.length];
960 s.setData(x);
961 for (int i = 0; i < p.length; i++) {
962
963 q[i] = s.evaluate(p[i] * 100);
964 }
965 return q;
966 };
967 } else if (STATISTICS.equals(name)) {
968 function = Quantile.withDefaults()::evaluate;
969 } else {
970 throw new IllegalStateException("Unknown double[] function: " + name);
971 }
972 }
973
974
975
976
977
978
979
980
981 private static double[] sortQuantile(double[] values, double[] p) {
982
983 final int n = values.length;
984 if (p.length == 0) {
985 throw new IllegalArgumentException("No quantiles specified for double[] data");
986 }
987 for (final double pp : p) {
988 checkQuantile(pp);
989 }
990
991 final double[] q = new double[p.length];
992 if (n <= 1) {
993 Arrays.fill(q, n == 0 ? Double.NaN : values[0]);
994 return q;
995 }
996
997 Arrays.sort(values);
998 for (int i = 0; i < p.length; i++) {
999
1000
1001 final double pos = p[i] * (n + 1);
1002 final double fpos = Math.floor(pos);
1003 final int j = (int) fpos;
1004 final double g = pos - fpos;
1005 if (j < 1) {
1006 q[i] = values[0];
1007 } else if (j >= n) {
1008 q[i] = values[n - 1];
1009 } else {
1010 q[i] = (1 - g) * values[j - 1] + g * values[j];
1011 }
1012 }
1013 return q;
1014 }
1015 }
1016
1017
1018
1019
1020
1021 @State(Scope.Benchmark)
1022 public static class IntQuantileFunctionSource {
1023
1024 @Param({JDK, CM3, CM4, STATISTICS})
1025 private String name;
1026
1027
1028 private BiFunction<int[], double[], double[]> function;
1029
1030
1031
1032
1033 public BiFunction<int[], double[], double[]> getFunction() {
1034 return function;
1035 }
1036
1037
1038
1039
1040 @Setup
1041 public void setup() {
1042
1043
1044 if (JDK.equals(name)) {
1045 function = IntQuantileFunctionSource::sortQuantile;
1046 } else if (STATISTICS.equals(name)) {
1047 function = Quantile.withDefaults()::evaluate;
1048 } else {
1049 throw new IllegalStateException("Unknown int[] function: " + name);
1050 }
1051 }
1052
1053
1054
1055
1056
1057
1058
1059
1060 private static double[] sortQuantile(int[] values, double[] p) {
1061
1062 final int n = values.length;
1063 if (p.length == 0) {
1064 throw new IllegalArgumentException("No quantiles specified for int[] data");
1065 }
1066 for (final double pp : p) {
1067 checkQuantile(pp);
1068 }
1069
1070 final double[] q = new double[p.length];
1071 if (n <= 1) {
1072 Arrays.fill(q, n == 0 ? Double.NaN : values[0]);
1073 return q;
1074 }
1075
1076 Arrays.sort(values);
1077 for (int i = 0; i < p.length; i++) {
1078
1079
1080 final double pos = p[i] * (n + 1);
1081 final double fpos = Math.floor(pos);
1082 final int j = (int) fpos;
1083 final double g = pos - fpos;
1084 if (j < 1) {
1085 q[i] = values[0];
1086 } else if (j >= n) {
1087 q[i] = values[n - 1];
1088 } else {
1089 q[i] = (1 - g) * values[j - 1] + g * values[j];
1090 }
1091 }
1092 return q;
1093 }
1094 }
1095
1096
1097
1098
1099
1100
1101
1102 private static void checkQuantile(double p) {
1103 if (!(p >= 0 && p <= 1)) {
1104 throw new IllegalArgumentException("Invalid quantile: " + p);
1105 }
1106 }
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 @Benchmark
1117 public void doubleQuantiles(DoubleQuantileFunctionSource function, DataSource source,
1118 QuantileSource quantiles, Blackhole bh) {
1119 final int size = source.size();
1120 final double[] p = quantiles.getData();
1121 final BinaryOperator<double[]> fun = function.getFunction();
1122 for (int j = -1; ++j < size;) {
1123 bh.consume(fun.apply(source.getData(j), p));
1124 }
1125 }
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135 @Benchmark
1136 public void doubleQuantileRange(DoubleQuantileFunctionSource function, DataSource source,
1137 QuantileRangeSource quantiles, Blackhole bh) {
1138 final int size = source.size();
1139 final double[] p = quantiles.getData();
1140 final BinaryOperator<double[]> fun = function.getFunction();
1141 for (int j = -1; ++j < size;) {
1142 bh.consume(fun.apply(source.getData(j), p));
1143 }
1144 }
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 @Benchmark
1155 public void intQuantiles(IntQuantileFunctionSource function, DataSource source,
1156 QuantileSource quantiles, Blackhole bh) {
1157 final int size = source.size();
1158 final double[] p = quantiles.getData();
1159 final BiFunction<int[], double[], double[]> fun = function.getFunction();
1160 for (int j = -1; ++j < size;) {
1161 bh.consume(fun.apply(source.getIntData(j), p));
1162 }
1163 }
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173 @Benchmark
1174 public void intQuantileRange(IntQuantileFunctionSource function, DataSource source,
1175 QuantileRangeSource quantiles, Blackhole bh) {
1176 final int size = source.size();
1177 final double[] p = quantiles.getData();
1178 final BiFunction<int[], double[], double[]> fun = function.getFunction();
1179 for (int j = -1; ++j < size;) {
1180 bh.consume(fun.apply(source.getIntData(j), p));
1181 }
1182 }
1183 }