1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.numbers.examples.jmh.complex;
19
20 import org.apache.commons.numbers.complex.Complex;
21 import org.apache.commons.rng.UniformRandomProvider;
22 import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler;
23 import org.apache.commons.rng.simple.RandomSource;
24 import org.openjdk.jmh.annotations.Benchmark;
25 import org.openjdk.jmh.annotations.BenchmarkMode;
26 import org.openjdk.jmh.annotations.Fork;
27 import org.openjdk.jmh.annotations.Measurement;
28 import org.openjdk.jmh.annotations.Mode;
29 import org.openjdk.jmh.annotations.OutputTimeUnit;
30 import org.openjdk.jmh.annotations.Param;
31 import org.openjdk.jmh.annotations.Scope;
32 import org.openjdk.jmh.annotations.Setup;
33 import org.openjdk.jmh.annotations.State;
34 import org.openjdk.jmh.annotations.Warmup;
35 import org.openjdk.jmh.infra.Blackhole;
36 import java.util.Arrays;
37 import java.util.concurrent.TimeUnit;
38 import java.util.function.BiFunction;
39 import java.util.function.Predicate;
40 import java.util.function.Supplier;
41 import java.util.function.ToDoubleFunction;
42 import java.util.function.UnaryOperator;
43 import java.util.stream.Stream;
44
45
46
47
48 @BenchmarkMode(Mode.AverageTime)
49 @OutputTimeUnit(TimeUnit.NANOSECONDS)
50 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
51 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
52 @State(Scope.Benchmark)
53 @Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx512M"})
54 public class ComplexPerformance {
55
56
57
58
59 private static final double[] EDGE_NUMBERS = {
60 Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.MAX_VALUE,
61 -Double.MAX_VALUE, Double.MIN_VALUE, -Double.MIN_VALUE, 0.0, -0.0, Double.NaN};
62
63
64 private static final double RANGE = 3.456789;
65
66
67
68
69 @State(Scope.Benchmark)
70 public static class ComplexNumberSize {
71
72
73
74 @Param({"10000"})
75 private int size;
76
77
78
79
80
81
82 public int getSize() {
83 return size;
84 }
85 }
86
87
88
89
90 @State(Scope.Benchmark)
91 public static class ComplexNumbers extends ComplexNumberSize {
92
93 protected Complex[] numbers;
94
95
96
97
98 @Param({"cis", "vector", "log-uniform", "uniform", "edge"})
99 private String type;
100
101
102
103
104
105
106 public Complex[] getNumbers() {
107 return numbers;
108 }
109
110
111
112
113 @Setup
114 public void setup() {
115 numbers = createNumbers(RandomSource.XO_RO_SHI_RO_128_PP.create());
116 }
117
118
119
120
121
122
123
124 Complex[] createNumbers(UniformRandomProvider rng) {
125 Supplier<Complex> generator;
126 if ("cis".equals(type)) {
127 generator = () -> Complex.ofCis(rng.nextDouble() * 2 * Math.PI);
128 } else if ("vector".equals(type)) {
129
130
131
132
133
134 final ZigguratNormalizedGaussianSampler s = ZigguratNormalizedGaussianSampler.of(rng);
135 generator = () -> Complex.ofCartesian(s.sample(), s.sample());
136 } else if ("log-uniform".equals(type)) {
137 generator = () -> Complex.ofCartesian(createLogUniformNumber(rng), createLogUniformNumber(rng));
138 } else if ("uniform".equals(type)) {
139 generator = () -> Complex.ofCartesian(createUniformNumber(rng), createUniformNumber(rng));
140 } else if ("edge".equals(type)) {
141 generator = () -> Complex.ofCartesian(createEdgeNumber(rng), createEdgeNumber(rng));
142 } else {
143 throw new IllegalStateException("Unknown number type: " + type);
144 }
145 return Stream.generate(generator).limit(getSize()).toArray(Complex[]::new);
146 }
147 }
148
149
150
151
152 @State(Scope.Benchmark)
153 public static class TwoComplexNumbers extends ComplexNumbers {
154
155 private Complex[] numbers2;
156
157
158
159
160
161
162 public Complex[] getNumbers2() {
163 return numbers2;
164 }
165
166
167
168
169 @Override
170 @Setup
171 public void setup() {
172
173 final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();
174 numbers = createNumbers(rng);
175 numbers2 = createNumbers(rng);
176 }
177 }
178
179
180
181
182 @State(Scope.Benchmark)
183 public static class ComplexAndRealNumbers extends ComplexNumbers {
184
185 private double[] numbers2;
186
187
188
189
190
191
192 public double[] getNumbers2() {
193 return numbers2;
194 }
195
196
197
198
199 @Override
200 @Setup
201 public void setup() {
202
203 final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();
204 numbers = createNumbers(rng);
205 numbers2 = Arrays.stream(createNumbers(rng)).mapToDouble(Complex::real).toArray();
206 }
207 }
208
209
210
211
212 private interface ComplexRealFunction {
213
214
215
216
217
218
219
220 Complex apply(Complex z, double x);
221 }
222
223
224
225
226
227
228
229
230
231
232 private static double createLogUniformNumber(UniformRandomProvider rng) {
233
234
235
236 final long mask = ((1L << 52) - 1) | 1L << 63;
237 final long bits = rng.nextLong() & mask;
238
239 final long exp = rng.nextInt(129) - 64 + 1023;
240 return Double.longBitsToDouble(bits | (exp << 52));
241 }
242
243
244
245
246
247
248
249 private static double createUniformNumber(UniformRandomProvider rng) {
250
251
252
253
254
255
256 return (rng.nextDouble() - rng.nextInt(1)) * RANGE;
257 }
258
259
260
261
262
263
264
265
266 private static double createEdgeNumber(UniformRandomProvider rng) {
267 return EDGE_NUMBERS[rng.nextInt(EDGE_NUMBERS.length)];
268 }
269
270
271
272
273
274
275
276
277 private static void apply(Complex[] numbers, Predicate<Complex> fun, Blackhole bh) {
278 for (int i = 0; i < numbers.length; i++) {
279 bh.consume(fun.test(numbers[i]));
280 }
281 }
282
283
284
285
286
287
288
289
290 private static void apply(Complex[] numbers, ToDoubleFunction<Complex> fun, Blackhole bh) {
291 for (int i = 0; i < numbers.length; i++) {
292 bh.consume(fun.applyAsDouble(numbers[i]));
293 }
294 }
295
296
297
298
299
300
301
302
303 private static void apply(Complex[] numbers, UnaryOperator<Complex> fun, Blackhole bh) {
304 for (int i = 0; i < numbers.length; i++) {
305 bh.consume(fun.apply(numbers[i]));
306 }
307 }
308
309
310
311
312
313
314
315
316
317 private static void apply(Complex[] numbers, Complex[] numbers2,
318 BiFunction<Complex, Complex, Complex> fun, Blackhole bh) {
319 for (int i = 0; i < numbers.length; i++) {
320 bh.consume(fun.apply(numbers[i], numbers2[i]));
321 }
322 }
323
324
325
326
327
328
329
330
331
332 private static void apply(Complex[] numbers, double[] numbers2,
333 ComplexRealFunction fun, Blackhole bh) {
334 for (int i = 0; i < numbers.length; i++) {
335 bh.consume(fun.apply(numbers[i], numbers2[i]));
336 }
337 }
338
339
340
341
342
343
344
345 private static Complex identity(Complex z) {
346 return z;
347 }
348
349
350
351
352
353
354
355
356 private static Complex copy(Complex z) {
357 return Complex.ofCartesian(z.real(), z.imag());
358 }
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379 public void real2(ComplexNumbers numbers, Blackhole bh) {
380 final Complex[] z = numbers.getNumbers();
381 for (int i = 0; i < z.length; i++) {
382 bh.consume(z[i].real());
383 }
384 }
385
386
387
388
389
390
391
392
393
394
395 public void conj2(ComplexNumbers numbers, Blackhole bh) {
396 final Complex[] z = numbers.getNumbers();
397 for (int i = 0; i < z.length; i++) {
398 bh.consume(z[i].conj());
399 }
400 }
401
402
403
404
405
406
407
408 @Benchmark
409 public void baselineIdentity(ComplexNumbers numbers, Blackhole bh) {
410 apply(numbers.getNumbers(), ComplexPerformance::identity, bh);
411 }
412
413
414
415
416
417
418
419
420
421 @Benchmark
422 public void baselineCopy(ComplexNumbers numbers, Blackhole bh) {
423 apply(numbers.getNumbers(), ComplexPerformance::copy, bh);
424 }
425
426
427
428 @Benchmark
429 public void isNaN(ComplexNumbers numbers, Blackhole bh) {
430 apply(numbers.getNumbers(), Complex::isNaN, bh);
431 }
432
433 @Benchmark
434 public void isInfinite(ComplexNumbers numbers, Blackhole bh) {
435 apply(numbers.getNumbers(), Complex::isInfinite, bh);
436 }
437
438 @Benchmark
439 public void isFinite(ComplexNumbers numbers, Blackhole bh) {
440 apply(numbers.getNumbers(), Complex::isFinite, bh);
441 }
442
443
444
445 @Benchmark
446 public void real(ComplexNumbers numbers, Blackhole bh) {
447 apply(numbers.getNumbers(), Complex::real, bh);
448 }
449
450 @Benchmark
451 public void imag(ComplexNumbers numbers, Blackhole bh) {
452 apply(numbers.getNumbers(), Complex::imag, bh);
453 }
454
455 @Benchmark
456 public void abs(ComplexNumbers numbers, Blackhole bh) {
457 apply(numbers.getNumbers(), Complex::abs, bh);
458 }
459
460 @Benchmark
461 public void arg(ComplexNumbers numbers, Blackhole bh) {
462 apply(numbers.getNumbers(), Complex::arg, bh);
463 }
464
465 @Benchmark
466 public void norm(ComplexNumbers numbers, Blackhole bh) {
467 apply(numbers.getNumbers(), Complex::norm, bh);
468 }
469
470
471
472
473
474
475
476
477
478
479 @Benchmark
480 public void sqrtNorm(ComplexNumbers numbers, Blackhole bh) {
481 apply(numbers.getNumbers(), (ToDoubleFunction<Complex>) z -> Math.sqrt(z.norm()), bh);
482 }
483
484
485
486
487
488
489
490
491 @Benchmark
492 public void absMathHypot(ComplexNumbers numbers, Blackhole bh) {
493 apply(numbers.getNumbers(), (ToDoubleFunction<Complex>) z -> Math.hypot(z.real(), z.imag()), bh);
494 }
495
496
497
498 @Benchmark
499 public void conj(ComplexNumbers numbers, Blackhole bh) {
500 apply(numbers.getNumbers(), Complex::conj, bh);
501 }
502
503 @Benchmark
504 public void negate(ComplexNumbers numbers, Blackhole bh) {
505 apply(numbers.getNumbers(), Complex::negate, bh);
506 }
507
508 @Benchmark
509 public void proj(ComplexNumbers numbers, Blackhole bh) {
510 apply(numbers.getNumbers(), Complex::proj, bh);
511 }
512
513 @Benchmark
514 public void cos(ComplexNumbers numbers, Blackhole bh) {
515 apply(numbers.getNumbers(), Complex::cos, bh);
516 }
517
518 @Benchmark
519 public void cosh(ComplexNumbers numbers, Blackhole bh) {
520 apply(numbers.getNumbers(), Complex::cosh, bh);
521 }
522
523 @Benchmark
524 public void exp(ComplexNumbers numbers, Blackhole bh) {
525 apply(numbers.getNumbers(), Complex::exp, bh);
526 }
527
528 @Benchmark
529 public void log(ComplexNumbers numbers, Blackhole bh) {
530 apply(numbers.getNumbers(), Complex::log, bh);
531 }
532
533 @Benchmark
534 public void log10(ComplexNumbers numbers, Blackhole bh) {
535 apply(numbers.getNumbers(), Complex::log10, bh);
536 }
537
538 @Benchmark
539 public void sin(ComplexNumbers numbers, Blackhole bh) {
540 apply(numbers.getNumbers(), Complex::sin, bh);
541 }
542
543 @Benchmark
544 public void sinh(ComplexNumbers numbers, Blackhole bh) {
545 apply(numbers.getNumbers(), Complex::sinh, bh);
546 }
547
548 @Benchmark
549 public void sqrt(ComplexNumbers numbers, Blackhole bh) {
550 apply(numbers.getNumbers(), Complex::sqrt, bh);
551 }
552
553 @Benchmark
554 public void tan(ComplexNumbers numbers, Blackhole bh) {
555 apply(numbers.getNumbers(), Complex::tan, bh);
556 }
557
558 @Benchmark
559 public void tanh(ComplexNumbers numbers, Blackhole bh) {
560 apply(numbers.getNumbers(), Complex::tanh, bh);
561 }
562
563 @Benchmark
564 public void acos(ComplexNumbers numbers, Blackhole bh) {
565 apply(numbers.getNumbers(), Complex::acos, bh);
566 }
567
568 @Benchmark
569 public void acosh(ComplexNumbers numbers, Blackhole bh) {
570 apply(numbers.getNumbers(), Complex::acosh, bh);
571 }
572
573 @Benchmark
574 public void asin(ComplexNumbers numbers, Blackhole bh) {
575 apply(numbers.getNumbers(), Complex::asin, bh);
576 }
577
578 @Benchmark
579 public void asinh(ComplexNumbers numbers, Blackhole bh) {
580 apply(numbers.getNumbers(), Complex::asinh, bh);
581 }
582
583 @Benchmark
584 public void atan(ComplexNumbers numbers, Blackhole bh) {
585 apply(numbers.getNumbers(), Complex::atan, bh);
586 }
587
588 @Benchmark
589 public void atanh(ComplexNumbers numbers, Blackhole bh) {
590 apply(numbers.getNumbers(), Complex::atanh, bh);
591 }
592
593
594
595 @Benchmark
596 public void pow(TwoComplexNumbers numbers, Blackhole bh) {
597 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::pow, bh);
598 }
599
600 @Benchmark
601 public void multiply(TwoComplexNumbers numbers, Blackhole bh) {
602 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::multiply, bh);
603 }
604
605 @Benchmark
606 public void divide(TwoComplexNumbers numbers, Blackhole bh) {
607 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::divide, bh);
608 }
609
610 @Benchmark
611 public void add(TwoComplexNumbers numbers, Blackhole bh) {
612 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::add, bh);
613 }
614
615 @Benchmark
616 public void subtract(TwoComplexNumbers numbers, Blackhole bh) {
617 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::subtract, bh);
618 }
619
620
621
622
623
624
625
626
627
628
629
630
631 @Benchmark
632 public void powReal(ComplexAndRealNumbers numbers, Blackhole bh) {
633 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::pow, bh);
634 }
635
636 @Benchmark
637 public void multiplyReal(ComplexAndRealNumbers numbers, Blackhole bh) {
638 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::multiply, bh);
639 }
640
641 @Benchmark
642 public void divideReal(ComplexAndRealNumbers numbers, Blackhole bh) {
643 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::divide, bh);
644 }
645
646 @Benchmark
647 public void addReal(ComplexAndRealNumbers numbers, Blackhole bh) {
648 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::add, bh);
649 }
650
651 @Benchmark
652 public void subtractReal(ComplexAndRealNumbers numbers, Blackhole bh) {
653 apply(numbers.getNumbers(), numbers.getNumbers2(), Complex::subtract, bh);
654 }
655 }