1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.rng.examples.jmh.core;
19
20 import java.util.concurrent.ThreadLocalRandom;
21 import java.util.concurrent.TimeUnit;
22 import java.util.function.LongSupplier;
23 import java.util.stream.LongStream;
24 import org.apache.commons.rng.JumpableUniformRandomProvider;
25 import org.apache.commons.rng.simple.RandomSource;
26 import org.openjdk.jmh.annotations.Benchmark;
27 import org.openjdk.jmh.annotations.BenchmarkMode;
28 import org.openjdk.jmh.annotations.Fork;
29 import org.openjdk.jmh.annotations.Level;
30 import org.openjdk.jmh.annotations.Measurement;
31 import org.openjdk.jmh.annotations.Mode;
32 import org.openjdk.jmh.annotations.OutputTimeUnit;
33 import org.openjdk.jmh.annotations.Param;
34 import org.openjdk.jmh.annotations.Scope;
35 import org.openjdk.jmh.annotations.Setup;
36 import org.openjdk.jmh.annotations.State;
37 import org.openjdk.jmh.annotations.Warmup;
38
39
40
41
42
43
44
45
46
47
48
49
50 @BenchmarkMode(Mode.AverageTime)
51 @OutputTimeUnit(TimeUnit.NANOSECONDS)
52 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
53 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
54 @State(Scope.Benchmark)
55 @Fork(value = 1, jvmArgs = { "-server", "-Xms128M", "-Xmx128M" })
56 public class LXMBenchmark {
57
58 static final long ML = 0xd605bbb58c8abbfdL;
59
60
61 private static final String BASELINE1 = "baseline1";
62
63 private static final String BASELINE2 = "baseline2";
64
65 private static final String BASELINE4 = "baseline4";
66
67 private static final String REFERENCE = "reference";
68
69 private static final String BRANCHLESS = "branchless";
70
71
72
73
74
75
76
77
78 @State(Scope.Benchmark)
79 public static class UnsignedMultiplyHighSource {
80
81 private static final long INT_TO_UNSIGNED_BYTE_MASK = 0xffff_ffffL;
82
83 private static final long X = ML >>> 32;
84
85 private static final long Y = ML & INT_TO_UNSIGNED_BYTE_MASK;
86
87
88
89
90 @Param({BASELINE1,
91
92
93
94
95 "unsignedMultiplyHigh", "unsignedMultiplyHighWithML",
96 "unsignedMultiplyHighML",
97 "unsignedMultiplyHighPlusMultiplyLow", "unsignedMultiplyHighAndLow",
98 })
99 private String method;
100
101
102
103
104 @Param({"true", "false"})
105 private boolean precompute;
106
107
108 private LongSupplier gen;
109
110
111
112
113
114
115 long next() {
116 return gen.getAsLong();
117 }
118
119
120
121
122 @Setup
123 public void setup() {
124 final JumpableUniformRandomProvider rng =
125 (JumpableUniformRandomProvider) RandomSource.XO_RO_SHI_RO_128_PP.create();
126
127 LongSupplier ga;
128 LongSupplier gb;
129
130
131 if (precompute) {
132 final long[] values = LongStream.generate(rng::nextLong).limit(1024).toArray();
133 class A implements LongSupplier {
134 private int i;
135 @Override
136 public long getAsLong() {
137 return values[i++ & 1023];
138 }
139 }
140 ga = new A();
141 class B implements LongSupplier {
142 private int i;
143 @Override
144 public long getAsLong() {
145
146 return values[(i += 3) & 1023];
147 }
148 }
149 gb = new B();
150 } else {
151 ga = rng::nextLong;
152 gb = rng.jump()::nextLong;
153 }
154
155 if (BASELINE1.equals(method)) {
156 gen = () -> ga.getAsLong();
157 } else if (BASELINE2.equals(method)) {
158 gen = () -> ga.getAsLong() ^ gb.getAsLong();
159 } else if ("mathMultiplyHigh".equals(method)) {
160 gen = () -> mathMultiplyHigh(ga.getAsLong(), gb.getAsLong());
161 } else if ("mathMultiplyHighWithML".equals(method)) {
162 gen = () -> mathMultiplyHigh(ga.getAsLong(), ML);
163 } else if ("mathUnsignedMultiplyHigh".equals(method)) {
164 gen = () -> mathUnsignedMultiplyHigh(ga.getAsLong(), gb.getAsLong());
165 } else if ("mathUnsignedMultiplyHighWithML".equals(method)) {
166 gen = () -> mathUnsignedMultiplyHigh(ga.getAsLong(), ML);
167 } else if ("unsignedMultiplyHigh".equals(method)) {
168 gen = () -> unsignedMultiplyHigh(ga.getAsLong(), gb.getAsLong());
169 } else if ("unsignedMultiplyHighWithML".equals(method)) {
170 gen = () -> unsignedMultiplyHigh(ga.getAsLong(), ML);
171 } else if ("unsignedMultiplyHighML".equals(method)) {
172
173
174
175
176 gen = () -> unsignedMultiplyHighML(ga.getAsLong());
177 } else if ("unsignedMultiplyHighPlusMultiplyLow".equals(method)) {
178 gen = () -> {
179 final long a = ga.getAsLong();
180 final long b = gb.getAsLong();
181 return unsignedMultiplyHigh(a, b) ^ (a * b);
182 };
183 } else if ("unsignedMultiplyHighAndLow".equals(method)) {
184
185
186
187
188 final long[] lo = {0};
189 gen = () -> {
190 final long a = ga.getAsLong();
191 final long b = gb.getAsLong();
192 final long hi = unsignedMultiplyHigh(a, b, lo);
193 return hi ^ lo[0];
194 };
195 } else {
196 throw new IllegalStateException("Unknown method: " + method);
197 }
198 }
199
200
201
202
203
204
205
206
207
208
209 static long mathMultiplyHigh(long a, long b) {
210
211
212
213
214
215
216
217 throw new NoSuchMethodError();
218 }
219
220
221
222
223
224
225
226
227
228
229 static long mathUnsignedMultiplyHigh(long a, long b) {
230
231
232 throw new NoSuchMethodError();
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 static long unsignedMultiplyHigh(long value1, long value2) {
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 final long a = value1 >>> 32;
291 final long b = value1 & INT_TO_UNSIGNED_BYTE_MASK;
292 final long x = value2 >>> 32;
293 final long y = value2 & INT_TO_UNSIGNED_BYTE_MASK;
294
295
296 final long by = b * y;
297 final long bx = b * x;
298 final long ay = a * y;
299 final long ax = a * x;
300
301
302 final long carry = (by >>> 32) +
303 (bx & INT_TO_UNSIGNED_BYTE_MASK) +
304 ay;
305
306
307
308 return (bx >>> 32) + (carry >>> 32) + ax;
309 }
310
311
312
313
314
315
316
317
318
319
320 static long unsignedMultiplyHigh(long value1, long value2, long[] low) {
321 final long a = value1 >>> 32;
322 final long b = value1 & INT_TO_UNSIGNED_BYTE_MASK;
323 final long x = value2 >>> 32;
324 final long y = value2 & INT_TO_UNSIGNED_BYTE_MASK;
325
326
327 final long by = b * y;
328 final long bx = b * x;
329 final long ay = a * y;
330 final long ax = a * x;
331
332 final long carry = (by >>> 32) +
333 (bx & INT_TO_UNSIGNED_BYTE_MASK) +
334 ay;
335
336 low[0] = (carry << 32) | (by & INT_TO_UNSIGNED_BYTE_MASK);
337
338 return (bx >>> 32) + (carry >>> 32) + ax;
339 }
340
341
342
343
344
345
346
347
348
349
350
351
352 static long unsignedMultiplyHighML(long value) {
353 final long a = value >>> 32;
354 final long b = value & INT_TO_UNSIGNED_BYTE_MASK;
355
356 final long by = b * Y;
357 final long bx = b * X;
358 final long ay = a * Y;
359 final long ax = a * X;
360
361
362 final long carry = (by >>> 32) +
363 (bx & INT_TO_UNSIGNED_BYTE_MASK) +
364 ay;
365
366 return (bx >>> 32) + (carry >>> 32) + ax;
367 }
368 }
369
370
371
372
373
374
375
376
377
378 @State(Scope.Benchmark)
379 public static class UnsignedMultiply128Source {
380
381 private static final long INT_TO_UNSIGNED_BYTE_MASK = 0xffffffffL;
382
383
384
385
386 @Param({BASELINE1,
387 "unsignedMultiplyHighPlusProducts",
388 "unsignedMultiply128AndLow",
389 "unsignedMultiplyHighPlusProducts (square)",
390 "unsignedSquareHighPlusProducts",
391 "unsignedMultiply128AndLow (square)",
392 "unsignedSquare128AndLow",
393 })
394 private String method;
395
396
397
398
399 @Param({"true", "false"})
400 private boolean precompute;
401
402
403 private LongSupplier gen;
404
405
406
407
408
409
410 long next() {
411 return gen.getAsLong();
412 }
413
414
415
416
417 @Setup
418 public void setup() {
419 final JumpableUniformRandomProvider rng =
420 (JumpableUniformRandomProvider) RandomSource.XO_RO_SHI_RO_128_PP.create();
421
422 LongSupplier ga;
423 LongSupplier gb;
424 LongSupplier gc;
425 LongSupplier gd;
426
427
428 if (precompute) {
429 final long[] values = LongStream.generate(rng::nextLong).limit(1024).toArray();
430 class Gen implements LongSupplier {
431 private int i;
432 private final int inc;
433 Gen(int inc) {
434 this.inc = inc;
435 }
436 @Override
437 public long getAsLong() {
438 return values[(i += inc) & 1023];
439 }
440 }
441
442 ga = new Gen(1);
443 gb = new Gen(3);
444 gc = new Gen(5);
445 gd = new Gen(7);
446 } else {
447 ga = rng::nextLong;
448 gb = rng.jump()::nextLong;
449 gc = rng.jump()::nextLong;
450 gd = rng.jump()::nextLong;
451 }
452
453 if (BASELINE2.equals(method)) {
454 gen = () -> ga.getAsLong() ^ gb.getAsLong();
455 } else if (BASELINE4.equals(method)) {
456 gen = () -> ga.getAsLong() ^ gb.getAsLong() ^ gc.getAsLong() ^ gd.getAsLong();
457 } else if ("unsignedMultiplyHighPlusProducts".equals(method)) {
458 gen = () -> {
459 final long a = ga.getAsLong();
460 final long b = gb.getAsLong();
461 final long c = gc.getAsLong();
462 final long d = gd.getAsLong();
463 final long hi = UnsignedMultiplyHighSource.unsignedMultiplyHigh(b, d) +
464 a * d + b * c;
465 final long lo = b * d;
466 return hi ^ lo;
467 };
468 } else if ("unsignedMultiply128AndLow".equals(method)) {
469
470
471
472
473 final long[] lo = {0};
474 gen = () -> {
475 final long a = ga.getAsLong();
476 final long b = gb.getAsLong();
477 final long c = gc.getAsLong();
478 final long d = gd.getAsLong();
479 final long hi = unsignedMultiply128(a, b, c, d, lo);
480 return hi ^ lo[0];
481 };
482 } else if ("unsignedMultiplyHighPlusProducts (square)".equals(method)) {
483 gen = () -> {
484 final long a = ga.getAsLong();
485 final long b = gb.getAsLong();
486 final long hi = UnsignedMultiplyHighSource.unsignedMultiplyHigh(b, b) +
487 2 * a * b;
488 final long lo = b * b;
489 return hi ^ lo;
490 };
491 } else if ("unsignedSquareHighPlusProducts".equals(method)) {
492 gen = () -> {
493 final long a = ga.getAsLong();
494 final long b = gb.getAsLong();
495 final long hi = unsignedSquareHigh(b) +
496 2 * a * b;
497 final long lo = b * b;
498 return hi ^ lo;
499 };
500 } else if ("unsignedMultiply128AndLow (square)".equals(method)) {
501 final long[] lo = {0};
502 gen = () -> {
503 final long a = ga.getAsLong();
504 final long b = gb.getAsLong();
505 final long hi = unsignedMultiply128(a, b, a, b, lo);
506 return hi ^ lo[0];
507 };
508 } else if ("unsignedSquare128AndLow".equals(method)) {
509
510
511
512
513
514 final long[] lo = {0};
515 gen = () -> {
516 final long a = ga.getAsLong();
517 final long b = gb.getAsLong();
518 final long hi = unsignedSquare128(a, b, lo);
519 return hi ^ lo[0];
520 };
521 } else {
522 throw new IllegalStateException("Unknown 128-bit method: " + method);
523 }
524 }
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545 static long unsignedMultiply128(long value1h, long value1l, long value2h, long value2l,
546 long[] low) {
547
548
549 final long a = value1l >>> 32;
550 final long b = value1l & INT_TO_UNSIGNED_BYTE_MASK;
551 final long x = value2l >>> 32;
552 final long y = value2l & INT_TO_UNSIGNED_BYTE_MASK;
553
554 final long by = b * y;
555 final long bx = b * x;
556 final long ay = a * y;
557 final long ax = a * x;
558
559
560 final long carry = (by >>> 32) +
561 (bx & INT_TO_UNSIGNED_BYTE_MASK) +
562 ay;
563 low[0] = (carry << 32) | (by & INT_TO_UNSIGNED_BYTE_MASK);
564 final long high = (bx >>> 32) + (carry >>> 32) + ax;
565
566
567 return high + value1h * value2l + value1l * value2h;
568 }
569
570
571
572
573
574
575
576
577
578
579
580
581
582 static long unsignedSquareHigh(long value) {
583 final long a = value >>> 32;
584 final long b = value & INT_TO_UNSIGNED_BYTE_MASK;
585
586 final long by = b * b;
587 final long bx = b * a;
588 final long ax = a * a;
589
590
591 final long carry = (by >>> 32) +
592 (bx & INT_TO_UNSIGNED_BYTE_MASK) +
593 bx;
594
595 return (bx >>> 32) + (carry >>> 32) + ax;
596 }
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614 static long unsignedSquare128(long valueh, long valuel, long[] low) {
615
616
617 final long a = valuel >>> 32;
618 final long b = valuel & INT_TO_UNSIGNED_BYTE_MASK;
619
620
621 final long by = b * b;
622 final long bx = b * a;
623
624 final long ax = a * a;
625
626
627 final long carry = (by >>> 32) +
628 (bx & INT_TO_UNSIGNED_BYTE_MASK) +
629 bx;
630 low[0] = (carry << 32) | (by & INT_TO_UNSIGNED_BYTE_MASK);
631 final long high = (bx >>> 32) + (carry >>> 32) + ax;
632
633
634 return high + 2 * valueh * valuel;
635 }
636 }
637
638
639
640
641
642
643 @State(Scope.Benchmark)
644 public static class LCG128Source {
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663 @Param({
664 REFERENCE,
665
666
667
668
669 BRANCHLESS,
670
671
672 })
673 private String method;
674
675
676
677
678
679
680
681
682 @Param({
683
684
685
686
687
688
689 "2305843009213693951", "4611686018427387903", "6917529027641081855",
690 "9223372036854775807", "-6917529027641081857", "-4611686018427387905",
691 "-2305843009213693953", "-1",
692 })
693 private long add;
694
695
696
697
698
699
700
701
702
703
704 @Param({
705
706
707
708
709
710
711
712 "2305843009213693952",
713
714
715
716
717 })
718 private long range;
719
720
721 private LongSupplier gen;
722
723
724
725
726
727
728 long next() {
729 return gen.getAsLong();
730 }
731
732
733
734
735 @Setup(Level.Iteration)
736 public void setup() {
737 final long ah = ThreadLocalRandom.current().nextLong();
738 long al = add;
739
740 if (range == Long.MIN_VALUE) {
741
742 al += ThreadLocalRandom.current().nextLong();
743 } else if (range > 0) {
744
745 al += ThreadLocalRandom.current().nextLong(range);
746 } else if (range < 0) {
747
748 al += ThreadLocalRandom.current().nextLong(-range) + Long.MIN_VALUE;
749 }
750
751
752 if (REFERENCE.equals(method)) {
753 gen = new ReferenceLcg128(ah, al)::getAsLong;
754 } else if ("referenceFinal".equals(method)) {
755 gen = new ReferenceLcg128Final(ah, al)::getAsLong;
756 } else if ("compareUnsigned".equals(method)) {
757 gen = new CompareUnsignedLcg128(ah, al)::getAsLong;
758 } else if ("conditional".equals(method)) {
759 gen = new ConditionalLcg128(ah, al)::getAsLong;
760 } else if ("conditionalFinal".equals(method)) {
761 gen = new ConditionalLcg128Final(ah, al)::getAsLong;
762 } else if (BRANCHLESS.equals(method)) {
763 gen = new BranchlessLcg128(ah, al)::getAsLong;
764 } else if ("branchlessFull".equals(method)) {
765 gen = new BranchlessFullLcg128(ah, al)::getAsLong;
766 } else if ("branchlessFullComposed".equals(method)) {
767 gen = new BranchlessFullComposedLcg128(ah, al)::getAsLong;
768 } else {
769 throw new IllegalStateException("Unknown LCG method: " + method);
770 }
771 }
772
773
774
775
776
777
778 abstract static class BaseLcg128 implements LongSupplier {
779
780 protected long lsh;
781
782 protected long lsl = 1;
783 }
784
785
786
787
788
789
790
791
792 static class ReferenceLcg128 extends BaseLcg128 {
793
794 private long lah;
795
796 private long lal;
797
798
799
800
801
802 ReferenceLcg128(long ah, long al) {
803 lah = ah;
804 lal = al | 1;
805 }
806
807 @Override
808 public long getAsLong() {
809
810
811 final long sh = lsh;
812 final long sl = lsl;
813 final long u = ML * sl;
814
815 lsh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
816
817 lsl = u + lal;
818
819 if (Long.compareUnsigned(lsl, u) < 0) {
820 ++lsh;
821 }
822 return sh;
823 }
824 }
825
826
827
828
829
830 static class ReferenceLcg128Final extends BaseLcg128 {
831
832 private final long lah;
833
834 private final long lal;
835
836
837
838
839
840 ReferenceLcg128Final(long ah, long al) {
841 lah = ah;
842 lal = al | 1;
843 }
844
845 @Override
846 public long getAsLong() {
847 final long sh = lsh;
848 final long sl = lsl;
849 final long u = ML * sl;
850
851 lsh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
852
853 lsl = u + lal;
854
855 if (Long.compareUnsigned(lsl, u) < 0) {
856 ++lsh;
857 }
858 return sh;
859 }
860 }
861
862
863
864
865
866
867
868
869
870 static class CompareUnsignedLcg128 extends BaseLcg128 {
871
872 private long lah;
873
874 private long lal;
875
876
877
878
879
880 CompareUnsignedLcg128(long ah, long al) {
881 lah = ah;
882 lal = al | 1;
883 }
884
885 @Override
886 public long getAsLong() {
887 final long sh = lsh;
888 final long sl = lsl;
889 final long u = ML * sl;
890
891 lsh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
892
893 lsl = u + lal;
894
895 if (lsl + Long.MIN_VALUE < u + Long.MIN_VALUE) {
896 ++lsh;
897 }
898 return sh;
899 }
900 }
901
902
903
904
905
906 static class ConditionalLcg128 extends BaseLcg128 {
907
908 private long lah;
909
910 private long lal;
911
912
913
914
915
916 ConditionalLcg128(long ah, long al) {
917 lah = ah;
918 lal = al | 1;
919 }
920
921 @Override
922 public long getAsLong() {
923 long sh = lsh;
924 long sl = lsl;
925 final long z = sh;
926 final long u = ML * sl;
927
928 sh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
929
930 sl = u + lal;
931
932 lsh = (sl + Long.MIN_VALUE < u + Long.MIN_VALUE ? 1 : 0) + sh;
933 lsl = sl;
934 return z;
935 }
936 }
937
938
939
940
941
942 static class ConditionalLcg128Final extends BaseLcg128 {
943
944 private final long lah;
945
946 private final long lal;
947
948
949
950
951
952 ConditionalLcg128Final(long ah, long al) {
953 lah = ah;
954 lal = al | 1;
955 }
956
957 @Override
958 public long getAsLong() {
959 long sh = lsh;
960 long sl = lsl;
961 final long z = sh;
962 final long u = ML * sl;
963
964 sh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
965
966 sl = u + lal;
967
968 lsh = (sl + Long.MIN_VALUE < u + Long.MIN_VALUE ? 1 : 0) + sh;
969 lsl = sl;
970 return z;
971 }
972 }
973
974
975
976
977
978
979
980
981 static class BranchlessLcg128 extends BaseLcg128 {
982
983 private long lah;
984
985 private long lal;
986
987
988
989
990
991 BranchlessLcg128(long ah, long al) {
992 lah = ah;
993 lal = al | 1;
994 }
995
996 @Override
997 public long getAsLong() {
998 long sh = lsh;
999 final long sl = lsl;
1000 final long z = sh;
1001 final long u = ML * sl;
1002
1003 sh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
1004
1005 final long al = lal;
1006 lsl = u + al;
1007
1008 lsh = sh + unsignedAddHigh(u, al);
1009 return z;
1010 }
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024 static long unsignedAddHigh(long left, long right) {
1025
1026
1027 return ((left >>> 1) + (right >>> 1) + (left & 1)) >>> -1;
1028 }
1029 }
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039 static class BranchlessFullLcg128 extends BaseLcg128 {
1040
1041 private long lah;
1042
1043 private long lal;
1044
1045
1046
1047
1048
1049 BranchlessFullLcg128(long ah, long al) {
1050 lah = ah;
1051 lal = al | 1;
1052 }
1053
1054 @Override
1055 public long getAsLong() {
1056 long sh = lsh;
1057 final long sl = lsl;
1058 final long z = sh;
1059 final long u = ML * sl;
1060
1061 sh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
1062
1063 final long al = lal;
1064 lsl = u + al;
1065
1066 lsh = sh + unsignedAddHigh(u, al);
1067 return z;
1068 }
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082 static long unsignedAddHigh(long left, long right) {
1083
1084
1085 return ((left >>> 32) + (right >>> 32) +
1086 (((left & 0xffff_ffffL) + (right & 0xffff_ffffL)) >>> 32)) >>> 32;
1087 }
1088 }
1089
1090
1091
1092
1093
1094
1095
1096 static class BranchlessFullComposedLcg128 extends BaseLcg128 {
1097
1098 private long lah;
1099
1100 private long lal;
1101
1102
1103
1104
1105
1106 BranchlessFullComposedLcg128(long ah, long al) {
1107 lah = ah;
1108 lal = al | 1;
1109 }
1110
1111 @Override
1112 public long getAsLong() {
1113 long sh = lsh;
1114 long sl = lsl;
1115 final long z = sh;
1116 final long u = ML * sl;
1117
1118 sh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
1119
1120
1121 final long leftLo = u & 0xffff_ffffL;
1122 final long leftHi = u >>> 32;
1123 final long rightLo = lal & 0xffff_ffffL;
1124 final long rightHi = lal >>> 32;
1125 final long lo = leftLo + rightLo;
1126 final long hi = leftHi + rightHi + (lo >>> 32);
1127
1128 sl = (hi << 32) | lo & 0xffff_ffffL;
1129 lsh = sh + (hi >>> 32);
1130 lsl = sl;
1131 return z;
1132 }
1133 }
1134 }
1135
1136
1137
1138
1139
1140 @State(Scope.Benchmark)
1141 public static class LXM128Source {
1142
1143 static final long X0 = 0xdeadbeefdeadbeefL;
1144
1145 static final long X1 = lea64(0xdeadbeefdeadbeefL);
1146
1147 static final long S0 = 0;
1148
1149 static final long S1 = 1;
1150
1151
1152
1153
1154 @Param({
1155 REFERENCE,
1156
1157 BRANCHLESS,
1158 })
1159 private String method;
1160
1161
1162 private LongSupplier gen;
1163
1164
1165
1166
1167
1168
1169 long next() {
1170 return gen.getAsLong();
1171 }
1172
1173
1174
1175
1176 @Setup(Level.Iteration)
1177 public void setup() {
1178 final long ah = ThreadLocalRandom.current().nextLong();
1179 final long al = ThreadLocalRandom.current().nextLong();
1180
1181 if (REFERENCE.equals(method)) {
1182 gen = new ReferenceLxm128(ah, al)::getAsLong;
1183 } else if (BRANCHLESS.equals(method)) {
1184 gen = new BranchlessLxm128(ah, al)::getAsLong;
1185 } else {
1186 throw new IllegalStateException("Unknown L method: " + method);
1187 }
1188 }
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 static long lea64(long x) {
1202 x = (x ^ (x >>> 32)) * 0xdaba0b6eb09322e3L;
1203 x = (x ^ (x >>> 32)) * 0xdaba0b6eb09322e3L;
1204 return x ^ (x >>> 32);
1205 }
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221 abstract static class BaseLxm128 implements LongSupplier {
1222
1223 protected long state0 = X0;
1224
1225 protected long state1 = X1;
1226
1227 protected long lsh = S0;
1228
1229 protected long lsl = S1;
1230
1231 protected long lah;
1232
1233 protected long lal;
1234
1235
1236
1237
1238
1239 BaseLxm128(long ah, long al) {
1240 lah = ah;
1241 lal = al | 1;
1242 }
1243 }
1244
1245
1246
1247
1248 static class ReferenceLxm128 extends BaseLxm128 {
1249
1250
1251
1252
1253 ReferenceLxm128(long ah, long al) {
1254 super(ah, al);
1255 }
1256
1257 @Override
1258 public long getAsLong() {
1259
1260
1261
1262
1263 final long s0 = state0;
1264 final long sh = lsh;
1265
1266
1267 final long z = lea64(sh + s0);
1268
1269
1270
1271 final long sl = lsl;
1272 final long u = ML * sl;
1273
1274 lsh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
1275
1276 lsl = u + lal;
1277
1278 if (Long.compareUnsigned(lsl, u) < 0) {
1279 ++lsh;
1280 }
1281
1282
1283 long s1 = state1;
1284
1285 s1 ^= s0;
1286 state0 = Long.rotateLeft(s0, 24) ^ s1 ^ (s1 << 16);
1287 state1 = Long.rotateLeft(s1, 37);
1288
1289 return z;
1290 }
1291 }
1292
1293
1294
1295
1296
1297
1298 static class BranchlessLxm128 extends BaseLxm128 {
1299
1300
1301
1302
1303 BranchlessLxm128(long ah, long al) {
1304 super(ah, al);
1305 }
1306
1307 @Override
1308 public long getAsLong() {
1309 final long s0 = state0;
1310 long sh = lsh;
1311
1312
1313 final long z = lea64(sh + s0);
1314
1315
1316
1317 final long sl = lsl;
1318 final long u = ML * sl;
1319
1320 sh = ML * sh + UnsignedMultiplyHighSource.unsignedMultiplyHigh(ML, sl) + sl + lah;
1321
1322 final long al = lal;
1323 lsl = u + al;
1324
1325
1326 lsh = sh + LCG128Source.BranchlessLcg128.unsignedAddHigh(u, al);
1327
1328
1329 long s1 = state1;
1330
1331 s1 ^= s0;
1332 state0 = Long.rotateLeft(s0, 24) ^ s1 ^ (s1 << 16);
1333 state1 = Long.rotateLeft(s1, 37);
1334
1335 return z;
1336 }
1337 }
1338 }
1339
1340
1341
1342
1343
1344
1345
1346 @Benchmark
1347 public long unsignedMultiply(UnsignedMultiplyHighSource data) {
1348 return data.next();
1349 }
1350
1351
1352
1353
1354
1355
1356
1357 @Benchmark
1358 public long unsignedMultiply128(UnsignedMultiply128Source data) {
1359 return data.next();
1360 }
1361
1362
1363
1364
1365
1366
1367
1368 @Benchmark
1369 public long lcg128(LCG128Source data) {
1370 return data.next();
1371 }
1372
1373
1374
1375
1376
1377
1378
1379 @Benchmark
1380 public long lxm128(LXM128Source data) {
1381 return data.next();
1382 }
1383 }