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.math3.util.FastMath;
21 import org.apache.commons.numbers.core.Precision;
22 import org.openjdk.jmh.annotations.Benchmark;
23 import org.openjdk.jmh.annotations.BenchmarkMode;
24 import org.openjdk.jmh.annotations.Fork;
25 import org.openjdk.jmh.annotations.Measurement;
26 import org.openjdk.jmh.annotations.Mode;
27 import org.openjdk.jmh.annotations.OutputTimeUnit;
28 import org.openjdk.jmh.annotations.Param;
29 import org.openjdk.jmh.annotations.Scope;
30 import org.openjdk.jmh.annotations.Setup;
31 import org.openjdk.jmh.annotations.State;
32 import org.openjdk.jmh.annotations.Warmup;
33 import org.openjdk.jmh.infra.Blackhole;
34 import java.util.SplittableRandom;
35 import java.util.concurrent.TimeUnit;
36 import java.util.function.DoubleSupplier;
37 import java.util.function.DoubleUnaryOperator;
38 import java.util.function.Supplier;
39 import java.util.stream.DoubleStream;
40
41
42
43
44
45
46
47 @BenchmarkMode(Mode.AverageTime)
48 @OutputTimeUnit(TimeUnit.NANOSECONDS)
49 @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
50 @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
51 @State(Scope.Benchmark)
52 @Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx512M"})
53 public class SinCosPerformance {
54
55
56
57
58 private static final double[] EDGE_NUMBERS = {
59 Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.0, -0.0, Double.NaN};
60
61
62
63
64 @State(Scope.Benchmark)
65 public static class NumberSize {
66
67
68
69 @Param({"1000"})
70 private int size;
71
72
73
74
75
76
77 public int getSize() {
78 return size;
79 }
80 }
81
82
83
84
85 public abstract static class BaseNumbers extends NumberSize {
86
87 protected double[] numbers;
88
89
90
91
92
93
94 public double[] getNumbers() {
95 return numbers;
96 }
97
98
99
100
101 @Setup
102 public void setup() {
103 numbers = createNumbers(new SplittableRandom());
104
105 for (final double x : numbers) {
106 final double sin = Math.sin(x);
107 assertEquals(sin, FastMath.sin(x), 1, () -> "sin " + x);
108 final double cos = Math.cos(x);
109 assertEquals(cos, FastMath.cos(x), 1, () -> "cos " + x);
110 }
111 }
112
113
114
115
116
117
118
119 protected abstract double[] createNumbers(SplittableRandom rng);
120 }
121
122
123
124
125 @State(Scope.Benchmark)
126 public static class Numbers extends BaseNumbers {
127
128
129
130 @Param({"pi", "pi/2", "random", "edge"})
131 private String type;
132
133
134 @Override
135 protected double[] createNumbers(SplittableRandom rng) {
136 DoubleSupplier generator;
137 if ("pi".equals(type)) {
138 generator = () -> rng.nextDouble() * 2 * Math.PI - Math.PI;
139 } else if ("pi/2".equals(type)) {
140 generator = () -> rng.nextDouble() * Math.PI - Math.PI / 2;
141 } else if ("random".equals(type)) {
142 generator = () -> createRandomNumber(rng);
143 } else if ("edge".equals(type)) {
144 generator = () -> createEdgeNumber(rng);
145 } else {
146 throw new IllegalStateException("Unknown number type: " + type);
147 }
148 return DoubleStream.generate(generator).limit(getSize()).toArray();
149 }
150 }
151
152
153
154
155 @State(Scope.Benchmark)
156 public static class UniformNumbers extends BaseNumbers {
157
158
159
160
161
162
163 @Param({"1.57079", "3.14159", "10", "100", "1e4", "1e8", "1e16", "1e32"})
164 private double range;
165
166
167 @Override
168 protected double[] createNumbers(SplittableRandom rng) {
169 return rng.doubles(getSize(), -range, range).toArray();
170 }
171 }
172
173
174
175
176
177
178
179
180
181 static void assertEquals(double x, double y, int maxUlps, Supplier<String> msg) {
182 if (!Precision.equalsIncludingNaN(x, y, maxUlps)) {
183 throw new AssertionError(msg.get() + ": " + x + " != " + y);
184 }
185 }
186
187
188
189
190
191
192
193
194
195 private static double createRandomNumber(SplittableRandom rng) {
196 return rng.nextDouble(-1e200, 1e200);
197 }
198
199
200
201
202
203
204
205
206 private static double createEdgeNumber(SplittableRandom rng) {
207 return EDGE_NUMBERS[rng.nextInt(EDGE_NUMBERS.length)];
208 }
209
210
211
212
213
214
215
216
217 private static void apply(double[] numbers, DoubleUnaryOperator fun, Blackhole bh) {
218 for (int i = 0; i < numbers.length; i++) {
219 bh.consume(fun.applyAsDouble(numbers[i]));
220 }
221 }
222
223
224
225
226
227
228
229 private static double identity(double z) {
230 return z;
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 public void mathSin2(Numbers numbers, Blackhole bh) {
248 final double[] x = numbers.getNumbers();
249 for (int i = 0; i < x.length; i++) {
250 bh.consume(Math.sin(x[i]));
251 }
252 }
253
254
255
256
257
258
259
260
261 @Benchmark
262 public void baselineIdentity(Numbers numbers, Blackhole bh) {
263 apply(numbers.getNumbers(), SinCosPerformance::identity, bh);
264 }
265
266
267
268
269
270
271
272 @Benchmark
273 public void mathSin(Numbers numbers, Blackhole bh) {
274 apply(numbers.getNumbers(), Math::sin, bh);
275 }
276
277
278
279
280
281
282
283 @Benchmark
284 public void mathCos(Numbers numbers, Blackhole bh) {
285 apply(numbers.getNumbers(), Math::cos, bh);
286 }
287
288
289
290
291
292
293
294 @Benchmark
295 public void fastMathSin(Numbers numbers, Blackhole bh) {
296 apply(numbers.getNumbers(), FastMath::sin, bh);
297 }
298
299
300
301
302
303
304
305 @Benchmark
306 public void fastMathCos(Numbers numbers, Blackhole bh) {
307 apply(numbers.getNumbers(), FastMath::cos, bh);
308 }
309
310
311
312
313
314
315
316 @Benchmark
317 public void rangeMathSin(UniformNumbers numbers, Blackhole bh) {
318 apply(numbers.getNumbers(), Math::sin, bh);
319 }
320
321
322
323
324
325
326
327 @Benchmark
328 public void rangeFastMathSin(UniformNumbers numbers, Blackhole bh) {
329 apply(numbers.getNumbers(), FastMath::sin, bh);
330 }
331 }