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.geometry.examples.jmh.euclidean;
18  
19  import java.util.concurrent.TimeUnit;
20  
21  import org.apache.commons.geometry.euclidean.oned.AffineTransformMatrix1D;
22  import org.apache.commons.geometry.euclidean.oned.Vector1D;
23  import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
24  import org.apache.commons.geometry.euclidean.threed.Vector3D;
25  import org.apache.commons.geometry.euclidean.twod.AffineTransformMatrix2D;
26  import org.apache.commons.geometry.euclidean.twod.Vector2D;
27  import org.apache.commons.geometry.examples.jmh.BenchmarkUtils;
28  import org.apache.commons.rng.UniformRandomProvider;
29  import org.apache.commons.rng.simple.RandomSource;
30  import org.openjdk.jmh.annotations.Benchmark;
31  import org.openjdk.jmh.annotations.BenchmarkMode;
32  import org.openjdk.jmh.annotations.Fork;
33  import org.openjdk.jmh.annotations.Level;
34  import org.openjdk.jmh.annotations.Measurement;
35  import org.openjdk.jmh.annotations.Mode;
36  import org.openjdk.jmh.annotations.OutputTimeUnit;
37  import org.openjdk.jmh.annotations.Param;
38  import org.openjdk.jmh.annotations.Scope;
39  import org.openjdk.jmh.annotations.Setup;
40  import org.openjdk.jmh.annotations.State;
41  import org.openjdk.jmh.annotations.Warmup;
42  
43  /** Benchmarks for
44   * {@link org.apache.commons.geometry.euclidean.AbstractAffineTransformMatrix AbstractAffineTransformMatrix}
45   * subclasses.
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  @Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx512M"})
52  public class AffineTransformMatrixPerformance {
53  
54      /** Input class providing random arrays of double values for transformation.
55       */
56      @State(Scope.Thread)
57      public static class TransformArrayInput {
58  
59          /** The number of elements in the input array. */
60          @Param({"6000", "600000"})
61          private int size;
62  
63          /** Array containing the input elements. */
64          private double[] array;
65  
66          /** Get the configured size of the input array.
67           * @return the configured size of the input array
68           */
69          public int getSize() {
70              return size;
71          }
72  
73          /** Get the input array.
74           * @return input array
75           */
76          public double[] getArray() {
77              return array;
78          }
79  
80          /** Set up the input array.
81           */
82          @Setup(Level.Iteration)
83          public void setup() {
84              final UniformRandomProvider rand = RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP);
85  
86              array = new double[size];
87  
88              for (int i = 0; i < array.length; ++i) {
89                  array[i] = BenchmarkUtils.randomDouble(rand);
90              }
91          }
92      }
93  
94      /** Input class providing a 1D transform matrix.
95       */
96      @State(Scope.Thread)
97      public static class TransformMatrixInput1D {
98  
99          /** Input transform matrix. */
100         private AffineTransformMatrix1D transform;
101 
102         /** Get the input transform matrix.
103          * @return the input transform matrix
104          */
105         public AffineTransformMatrix1D getTransform() {
106             return transform;
107         }
108 
109         /** Set up the input. */
110         @Setup
111         public void setup() {
112             final UniformRandomProvider rand = RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP);
113 
114             transform = AffineTransformMatrix1D.of(BenchmarkUtils.randomDoubleArray(2, rand));
115         }
116     }
117 
118     /** Input class providing a 2D transform matrix.
119      */
120     @State(Scope.Thread)
121     public static class TransformMatrixInput2D {
122 
123         /** Input transform matrix. */
124         private AffineTransformMatrix2D transform;
125 
126         /** Get the input transform matrix.
127          * @return the input transform matrix
128          */
129         public AffineTransformMatrix2D getTransform() {
130             return transform;
131         }
132 
133         /** Set up the input. */
134         @Setup
135         public void setup() {
136             final UniformRandomProvider rand = RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP);
137 
138             transform = AffineTransformMatrix2D.of(BenchmarkUtils.randomDoubleArray(6, rand));
139         }
140     }
141 
142     /** Input class providing a 3D transform matrix.
143      */
144     @State(Scope.Thread)
145     public static class TransformMatrixInput3D {
146 
147         /** Input transform matrix. */
148         private AffineTransformMatrix3D transform;
149 
150         /** Get the input transform matrix.
151          * @return the input transform matrix
152          */
153         public AffineTransformMatrix3D getTransform() {
154             return transform;
155         }
156 
157         /** Set up the input. */
158         @Setup
159         public void setup() {
160             final UniformRandomProvider rand = RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP);
161 
162             transform = AffineTransformMatrix3D.of(BenchmarkUtils.randomDoubleArray(12, rand));
163         }
164     }
165 
166     /** Baseline benchmark for 1D transforms on array data.
167      * @param arrayInput array input
168      * @return transformed output
169      */
170     @Benchmark
171     public double[] baselineArray1D(final TransformArrayInput arrayInput) {
172         final double[] arr = arrayInput.getArray();
173 
174         double x;
175         for (int i = 0; i < arr.length; ++i) {
176             x = arr[i];
177 
178             arr[i] = x + 1;
179         }
180 
181         return arr;
182     }
183 
184     /** Benchmark testing the performance of transforming an array of doubles by converting each group
185      * to a Vector1D.
186      * @param arrayInput array input
187      * @param transformInput transform input
188      * @return transformed output
189      */
190     @Benchmark
191     public double[] transformArrayAsVectors1D(final TransformArrayInput arrayInput,
192             final TransformMatrixInput1D transformInput) {
193         final double[] arr = arrayInput.getArray();
194         final AffineTransformMatrix1D t = transformInput.getTransform();
195 
196         Vector1D in;
197         Vector1D out;
198         for (int i = 0; i < arr.length; ++i) {
199             in = Vector1D.of(arr[i]);
200 
201             out = t.apply(in);
202 
203             arr[i] = out.getX();
204         }
205 
206         return arr;
207     }
208 
209     /** Benchmark testing the performance of transforming an array of doubles by transforming
210      * the components directly.
211      * @param arrayInput array input
212      * @param transformInput transform input
213      * @return transformed output
214      */
215     @Benchmark
216     public double[] transformArrayComponents1D(final TransformArrayInput arrayInput,
217             final TransformMatrixInput1D transformInput) {
218         final double[] arr = arrayInput.getArray();
219         final AffineTransformMatrix1D t = transformInput.getTransform();
220 
221         double x;
222         for (int i = 0; i < arr.length; ++i) {
223             x = arr[i];
224 
225             arr[i] = t.applyX(x);
226         }
227 
228         return arr;
229     }
230 
231     /** Baseline benchmark for 2D transforms on array data.
232      * @param arrayInput array input
233      * @return transformed output
234      */
235     @Benchmark
236     public double[] baselineArray2D(final TransformArrayInput arrayInput) {
237         final double[] arr = arrayInput.getArray();
238 
239         double x;
240         double y;
241         for (int i = 0; i < arr.length; i += 2) {
242             x = arr[i];
243             y = arr[i + 1];
244 
245             arr[i] = x + 1;
246             arr[i + 1] = y + 1;
247         }
248 
249         return arr;
250     }
251 
252     /** Benchmark testing the performance of transforming an array of doubles by converting each group
253      * to a Vector2D.
254      * @param arrayInput array input
255      * @param transformInput transform input
256      * @return transformed output
257      */
258     @Benchmark
259     public double[] transformArrayAsVectors2D(final TransformArrayInput arrayInput,
260             final TransformMatrixInput2D transformInput) {
261         final double[] arr = arrayInput.getArray();
262         final AffineTransformMatrix2D t = transformInput.getTransform();
263 
264         Vector2D in;
265         Vector2D out;
266         for (int i = 0; i < arr.length; i += 2) {
267             in = Vector2D.of(
268                     arr[i],
269                     arr[i + 1]);
270 
271             out = t.apply(in);
272 
273             arr[i] = out.getX();
274             arr[i + 1] = out.getY();
275         }
276 
277         return arr;
278     }
279 
280     /** Benchmark testing the performance of transforming an array of doubles by transforming
281      * the components directly.
282      * @param arrayInput array input
283      * @param transformInput transform input
284      * @return transformed output
285      */
286     @Benchmark
287     public double[] transformArrayComponents2D(final TransformArrayInput arrayInput,
288             final TransformMatrixInput2D transformInput) {
289         final double[] arr = arrayInput.getArray();
290         final AffineTransformMatrix2D t = transformInput.getTransform();
291 
292         double x;
293         double y;
294         for (int i = 0; i < arr.length; i += 2) {
295             x = arr[i];
296             y = arr[i + 1];
297 
298             arr[i] = t.applyX(x, y);
299             arr[i + 1] = t.applyY(x, y);
300         }
301 
302         return arr;
303     }
304 
305     /** Baseline benchmark for 3D transforms on array data.
306      * @param arrayInput array input
307      * @return transformed output
308      */
309     @Benchmark
310     public double[] baselineArray3D(final TransformArrayInput arrayInput) {
311         final double[] arr = arrayInput.getArray();
312 
313         double x;
314         double y;
315         double z;
316         for (int i = 0; i < arr.length; i += 3) {
317             x = arr[i];
318             y = arr[i + 1];
319             z = arr[i + 2];
320 
321             arr[i] = x + 1;
322             arr[i + 1] = y + 1;
323             arr[i + 2] = z + 1;
324         }
325 
326         return arr;
327     }
328 
329     /** Benchmark testing the performance of transforming an array of doubles by converting each group
330      * to a Vector3D.
331      * @param arrayInput array input
332      * @param transformInput transform input
333      * @return transformed output
334      */
335     @Benchmark
336     public double[] transformArrayAsVectors3D(final TransformArrayInput arrayInput,
337             final TransformMatrixInput3D transformInput) {
338         final double[] arr = arrayInput.getArray();
339         final AffineTransformMatrix3D t = transformInput.getTransform();
340 
341         Vector3D in;
342         Vector3D out;
343         for (int i = 0; i < arr.length; i += 3) {
344             in = Vector3D.of(
345                     arr[i],
346                     arr[i + 1],
347                     arr[i + 2]);
348 
349             out = t.apply(in);
350 
351             arr[i] = out.getX();
352             arr[i + 1] = out.getY();
353             arr[i + 2] = out.getZ();
354         }
355 
356         return arr;
357     }
358 
359     /** Benchmark testing the performance of transforming an array of doubles by transforming
360      * the components directly.
361      * @param arrayInput array input
362      * @param transformInput transform input
363      * @return transformed output
364      */
365     @Benchmark
366     public double[] transformArrayComponents3D(final TransformArrayInput arrayInput,
367             final TransformMatrixInput3D transformInput) {
368         final double[] arr = arrayInput.getArray();
369         final AffineTransformMatrix3D t = transformInput.getTransform();
370 
371         double x;
372         double y;
373         double z;
374         for (int i = 0; i < arr.length; i += 3) {
375             x = arr[i];
376             y = arr[i + 1];
377             z = arr[i + 2];
378 
379             arr[i] = t.applyX(x, y, z);
380             arr[i + 1] = t.applyY(x, y, z);
381             arr[i + 2] = t.applyZ(x, y, z);
382         }
383 
384         return arr;
385     }
386 }