View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.numbers.examples.jmh.core;
18  
19  import java.util.concurrent.TimeUnit;
20  import java.util.function.ToDoubleFunction;
21  
22  import org.apache.commons.numbers.core.Norm;
23  import org.apache.commons.rng.UniformRandomProvider;
24  import org.apache.commons.rng.simple.RandomSource;
25  import org.openjdk.jmh.annotations.Benchmark;
26  import org.openjdk.jmh.annotations.BenchmarkMode;
27  import org.openjdk.jmh.annotations.Fork;
28  import org.openjdk.jmh.annotations.Measurement;
29  import org.openjdk.jmh.annotations.Mode;
30  import org.openjdk.jmh.annotations.OutputTimeUnit;
31  import org.openjdk.jmh.annotations.Param;
32  import org.openjdk.jmh.annotations.Scope;
33  import org.openjdk.jmh.annotations.Setup;
34  import org.openjdk.jmh.annotations.State;
35  import org.openjdk.jmh.annotations.Warmup;
36  import org.openjdk.jmh.infra.Blackhole;
37  
38  /**
39   * Execute benchmarks for the methods in the {@link Norm} class.
40   */
41  @BenchmarkMode(Mode.AverageTime)
42  @OutputTimeUnit(TimeUnit.NANOSECONDS)
43  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
44  @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
45  @State(Scope.Benchmark)
46  @Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx512M"})
47  public class NormPerformance {
48  
49      /** Class providing input vectors for benchmarks.
50       */
51      @State(Scope.Benchmark)
52      public static class VectorArrayInput {
53  
54          /** Number of vector samples. */
55          @Param("100000")
56          private int samples;
57  
58          /** Minimum possible double exponent. */
59          @Param("-550")
60          private int minExp;
61  
62          /** Maximum possible double exponent. */
63          @Param("+550")
64          private int maxExp;
65  
66          /** Range of exponents within a single vector. */
67          @Param("26")
68          private int vectorExpRange;
69  
70          /** Array of input vectors. */
71          private double[][] vectors;
72  
73          /** Get the input vectors.
74           * @return input vectors
75           */
76          public double[][] getVectors() {
77              return vectors;
78          }
79  
80          /** Create the input vectors for the instance.
81           */
82          @Setup
83          public void createVectors() {
84              final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_1024_PP.create();
85  
86              vectors = new double[samples][];
87              for (int i = 0; i < vectors.length; ++i) {
88                  // pick a general range for the vector element exponents and then
89                  // create values within that range
90                  final int vMidExp = rng.nextInt(maxExp - minExp + 1) + minExp;
91                  final int vExpRadius = vectorExpRange / 2;
92                  final int vMinExp = vMidExp - vExpRadius;
93                  final int vMaxExp = vMidExp + vExpRadius;
94  
95                  vectors[i] = DoubleUtils.randomArray(getLength(), vMinExp, vMaxExp, rng);
96              }
97          }
98  
99          /** Get the length of the input vectors.
100          * @return input vector length
101          */
102         protected int getLength() {
103             return 3;
104         }
105     }
106 
107     /** Class providing 2D input vectors for benchmarks.
108      */
109     @State(Scope.Benchmark)
110     public static class VectorArrayInput2D extends VectorArrayInput {
111 
112         /** {@inheritDoc} */
113         @Override
114         protected int getLength() {
115             return 2;
116         }
117     }
118 
119     /** Evaluate a norm computation method with the given input.
120      * @param fn function to evaluate
121      * @param input computation input
122      * @param bh blackhole
123      */
124     private static void eval(final ToDoubleFunction<double[]> fn,
125                              final VectorArrayInput input,
126                              final Blackhole bh) {
127         final double[][] vectors = input.getVectors();
128         for (int i = 0; i < vectors.length; ++i) {
129             bh.consume(fn.applyAsDouble(vectors[i]));
130         }
131     }
132 
133     /** Evaluate a norm computation method with the given input.
134      * @param fn function to evaluate
135      * @param input computation input
136      * @param bh blackhole
137      */
138     private static void eval(final Norm fn,
139                              final VectorArrayInput input,
140                              final Blackhole bh) {
141         final double[][] vectors = input.getVectors();
142         for (int i = 0; i < vectors.length; ++i) {
143             bh.consume(fn.of(vectors[i]));
144         }
145     }
146 
147     /** Compute the Euclidean norm directly with no checks for overflow or underflow.
148      * @param v input vector
149      * @return Euclidean norm
150      */
151     private static double directEuclideanNorm(final double[] v) {
152         double n = 0;
153         for (int i = 0; i < v.length; i++) {
154             n += v[i] * v[i];
155         }
156         return Math.sqrt(n);
157     }
158 
159     /** Compute a baseline performance metric with a method that does nothing.
160      * @param input benchmark input
161      * @param bh blackhole
162      */
163     @Benchmark
164     public void baseline(final VectorArrayInput input, final Blackhole bh) {
165         eval(v -> 0d, input, bh);
166     }
167 
168     /** Compute a baseline performance metric using direct computation of the
169      * Euclidean norm.
170      * @param input benchmark input
171      * @param bh blackhole
172      */
173     @Benchmark
174     public void directEuclideanArray(final VectorArrayInput input, final Blackhole bh) {
175         eval(NormPerformance::directEuclideanNorm, input, bh);
176     }
177 
178     /** Compute a baseline performance metric using {@link Math#hypot(double, double)}.
179      * @param input benchmark input
180      * @param bh blackhole
181      */
182     @Benchmark
183     public void hypot(final VectorArrayInput2D input, final Blackhole bh) {
184         eval(v -> Math.hypot(v[0], v[1]), input, bh);
185     }
186 
187     /** Compute the performance of the {@link Norm#L2} 2D method.
188      * @param input benchmark input
189      * @param bh blackhole
190      */
191     @Benchmark
192     public void euclidean2d(final VectorArrayInput2D input, final Blackhole bh) {
193         eval(v -> Norm.L2.of(v[0], v[1]), input, bh);
194     }
195 
196     /** Compute the performance of the {@link Norm#L2} 3D norm computation.
197      * @param input benchmark input
198      * @param bh blackhole
199      */
200     @Benchmark
201     public void euclidean3d(final VectorArrayInput input, final Blackhole bh) {
202         eval(v -> Norm.L2.of(v[0], v[1], v[2]), input, bh);
203     }
204 
205     /** Compute the performance of the {@link Norm#L2} array norm method.
206      * @param input benchmark input
207      * @param bh blackhole
208      */
209     @Benchmark
210     public void euclideanArray(final VectorArrayInput input, final Blackhole bh) {
211         eval(Norm.L2, input, bh);
212     }
213 }