1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.rng.examples.jmh.core;
18
19 import java.math.BigInteger;
20 import java.util.function.LongSupplier;
21 import org.apache.commons.rng.UniformRandomProvider;
22 import org.apache.commons.rng.examples.jmh.core.LXMBenchmark.LCG128Source;
23 import org.apache.commons.rng.examples.jmh.core.LXMBenchmark.LXM128Source;
24 import org.apache.commons.rng.examples.jmh.core.LXMBenchmark.UnsignedMultiply128Source;
25 import org.apache.commons.rng.examples.jmh.core.LXMBenchmark.UnsignedMultiplyHighSource;
26 import org.apache.commons.rng.simple.RandomSource;
27 import org.junit.jupiter.api.Assertions;
28 import org.junit.jupiter.api.RepeatedTest;
29 import org.junit.jupiter.api.Test;
30 import org.junit.jupiter.api.condition.EnabledForJreRange;
31 import org.junit.jupiter.api.condition.JRE;
32 import org.opentest4j.TestAbortedException;
33
34
35
36
37
38 class LXMBenchmarkTest {
39
40 private static final BigInteger TWO_POW_63 = BigInteger.ONE.shiftLeft(63);
41
42
43
44
45 interface LongLongFunction {
46
47
48
49
50
51
52
53 long apply(long a, long b);
54 }
55
56
57
58
59 interface LongLongLongLongFunction {
60
61
62
63
64
65
66
67
68
69 long apply(long a, long b, long c, long d);
70 }
71
72 @Test
73 @EnabledForJreRange(min = JRE.JAVA_9)
74 void testMathMultiplyHigh() {
75 try {
76 assertUnsignedMultiply(UnsignedMultiplyHighSource::mathMultiplyHigh, null);
77 } catch (NoSuchMethodError e) {
78 throw new TestAbortedException("Update mathMultiplyHigh to call Math.multiplyHigh");
79 }
80 }
81
82 @Test
83
84 @EnabledForJreRange(min = JRE.OTHER)
85 void testMathUnsignedMultiplyHigh() {
86 try {
87 assertUnsignedMultiply(UnsignedMultiplyHighSource::mathUnsignedMultiplyHigh, null);
88 } catch (NoSuchMethodError e) {
89 throw new TestAbortedException("Update mathUnsignedMultiplyHigh to call Math.unsignedMultiplyHigh");
90 }
91 }
92
93 @Test
94 void testUnsignedMultiplyHigh() {
95 assertUnsignedMultiply(UnsignedMultiplyHighSource::unsignedMultiplyHigh, null);
96 }
97
98 @Test
99 void testUnsignedMultiplyHighWithLow() {
100 final long[] lo = {0};
101 assertUnsignedMultiply((a, b) -> UnsignedMultiplyHighSource.unsignedMultiplyHigh(a, b, lo), lo);
102 }
103
104 private static void assertUnsignedMultiply(LongLongFunction fun, long[] lo) {
105 final UniformRandomProvider rng = RandomSource.JSF_64.create();
106 for (int i = 0; i < 100; i++) {
107 final long a = rng.nextLong();
108 final long b = rng.nextLong();
109 Assertions.assertEquals(unsignedMultiplyHigh(a, b),
110 fun.apply(a, b));
111 if (lo != null) {
112 Assertions.assertEquals(a * b, lo[0]);
113 }
114 }
115 }
116
117 @Test
118 void testUnsignedMultiplyHighML() {
119 final UniformRandomProvider rng = RandomSource.JSF_64.create();
120 final long b = LXMBenchmark.ML;
121 for (int i = 0; i < 100; i++) {
122 final long a = rng.nextLong();
123 Assertions.assertEquals(unsignedMultiplyHigh(a, b),
124 UnsignedMultiplyHighSource.unsignedMultiplyHighML(a));
125 }
126 }
127
128 @Test
129 void testUnsignedMultiplyHighWithProducts() {
130 assertUnsignedMultiply128((a, b, c, d) ->
131 UnsignedMultiplyHighSource.unsignedMultiplyHigh(b, d) +
132 a * d + b * c, null);
133 }
134
135 @Test
136 void testUnsignedMultiply128() {
137 final long[] lo = {0};
138 assertUnsignedMultiply128((a, b, c, d) -> UnsignedMultiply128Source.unsignedMultiply128(a, b, c, d, lo), lo);
139 }
140
141 private static void assertUnsignedMultiply128(LongLongLongLongFunction fun, long[] lo) {
142 final UniformRandomProvider rng = RandomSource.JSF_64.create();
143 for (int i = 0; i < 100; i++) {
144 final long a = rng.nextLong();
145 final long b = rng.nextLong();
146 final long c = rng.nextLong();
147 final long d = rng.nextLong();
148 Assertions.assertEquals(unsignedMultiplyHigh(a, b, c, d),
149 fun.apply(a, b, c, d));
150 if (lo != null) {
151 Assertions.assertEquals(b * d, lo[0]);
152 }
153 }
154 }
155
156 @Test
157 void testUnsignedSquareHigh() {
158 final UniformRandomProvider rng = RandomSource.JSF_64.create();
159 for (int i = 0; i < 100; i++) {
160 final long a = rng.nextLong();
161 Assertions.assertEquals(unsignedMultiplyHigh(a, a),
162 UnsignedMultiply128Source.unsignedSquareHigh(a));
163 }
164 }
165
166 @Test
167 void testUnsignedSquare128() {
168 final long[] lo = {0};
169 final UniformRandomProvider rng = RandomSource.JSF_64.create();
170 for (int i = 0; i < 100; i++) {
171 final long a = rng.nextLong();
172 final long b = rng.nextLong();
173 Assertions.assertEquals(unsignedMultiplyHigh(a, b, a, b),
174 UnsignedMultiply128Source.unsignedSquare128(a, b, lo));
175 Assertions.assertEquals(b * b, lo[0]);
176 }
177 }
178
179
180
181
182
183
184
185
186 private static long unsignedMultiplyHigh(long a, long b) {
187 final BigInteger ba = toUnsignedBigInteger(a);
188 final BigInteger bb = toUnsignedBigInteger(b);
189 return ba.multiply(bb).shiftRight(64).longValue();
190 }
191
192
193
194
195
196
197
198
199
200
201
202 private static long unsignedMultiplyHigh(long ah, long al, long bh, long bl) {
203 final BigInteger a = toUnsignedBigInteger(ah, al);
204 final BigInteger b = toUnsignedBigInteger(bh, bl);
205 return a.multiply(b).shiftRight(64).longValue();
206 }
207
208
209
210
211
212
213
214 private static BigInteger toUnsignedBigInteger(long v) {
215 return v < 0 ?
216 TWO_POW_63.add(BigInteger.valueOf(v & Long.MAX_VALUE)) :
217 BigInteger.valueOf(v);
218 }
219
220
221
222
223
224
225
226
227 private static BigInteger toUnsignedBigInteger(long hi, long lo) {
228 return toUnsignedBigInteger(hi).shiftLeft(64).add(toUnsignedBigInteger(lo));
229 }
230
231
232
233
234 @RepeatedTest(value = 50)
235 void testLcg128() {
236 final long ah = RandomSource.createLong();
237 final long al = RandomSource.createLong() | 1;
238 final LongSupplier a = new LCG128Source.ReferenceLcg128(ah, al)::getAsLong;
239 final LongSupplier[] b = new LongSupplier[] {
240 new LCG128Source.ReferenceLcg128(ah, al)::getAsLong,
241 new LCG128Source.ReferenceLcg128Final(ah, al)::getAsLong,
242 new LCG128Source.CompareUnsignedLcg128(ah, al)::getAsLong,
243 new LCG128Source.ConditionalLcg128(ah, al)::getAsLong,
244 new LCG128Source.ConditionalLcg128Final(ah, al)::getAsLong,
245 new LCG128Source.BranchlessLcg128(ah, al)::getAsLong,
246 new LCG128Source.BranchlessFullLcg128(ah, al)::getAsLong,
247 new LCG128Source.BranchlessFullComposedLcg128(ah, al)::getAsLong,
248 };
249 for (int i = 0; i < 10; i++) {
250 final long expected = a.getAsLong();
251 for (int j = 0; j < b.length; j++) {
252 Assertions.assertEquals(expected, b[j].getAsLong());
253 }
254 }
255 }
256
257
258
259
260 @RepeatedTest(value = 50)
261 void testLxm128() {
262 final long ah = RandomSource.createLong();
263 final long al = RandomSource.createLong();
264
265
266 final long[] seed = {ah, al,
267 LXM128Source.S0, LXM128Source.S1,
268 LXM128Source.X0, LXM128Source.X1};
269 final UniformRandomProvider rng = RandomSource.L128_X128_MIX.create(seed);
270 final LongSupplier a = rng::nextLong;
271 final LongSupplier[] b = new LongSupplier[] {
272 new LXM128Source.ReferenceLxm128(ah, al)::getAsLong,
273 new LXM128Source.BranchlessLxm128(ah, al)::getAsLong,
274 };
275 for (int i = 0; i < 10; i++) {
276 final long expected = a.getAsLong();
277 for (int j = 0; j < b.length; j++) {
278 Assertions.assertEquals(expected, b[j].getAsLong());
279 }
280 }
281 }
282 }