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  
18  package org.apache.commons.rng.examples.jmh.core;
19  
20  import org.apache.commons.rng.UniformRandomProvider;
21  import org.openjdk.jmh.annotations.Benchmark;
22  import org.openjdk.jmh.annotations.BenchmarkMode;
23  import org.openjdk.jmh.annotations.Fork;
24  import org.openjdk.jmh.annotations.Measurement;
25  import org.openjdk.jmh.annotations.Mode;
26  import org.openjdk.jmh.annotations.OutputTimeUnit;
27  import org.openjdk.jmh.annotations.Param;
28  import org.openjdk.jmh.annotations.Scope;
29  import org.openjdk.jmh.annotations.State;
30  import org.openjdk.jmh.annotations.Warmup;
31  import org.openjdk.jmh.infra.Blackhole;
32  
33  import java.util.concurrent.TimeUnit;
34  
35  /**
36   * Benchmarks to check linearity in the baseline implementations of {@link UniformRandomProvider}.
37   *
38   * <p>These ordinarily do not need to be run. The benchmarks can be used to determine
39   * if the baseline scales linearly with workload. If not then the JVM has removed the
40   * baseline from the testing loop given that its result is predictable. The ideal
41   * baseline will:</p>
42   *
43   * <ul>
44   *  <li>Run as fast as possible
45   *  <li>Not be removed from the execution path
46   * </ul>
47   *
48   * <p>The results of this benchmark should be plotted for each method using [numValues] vs [run time]
49   * to check linearity.</p>
50   */
51  @BenchmarkMode(Mode.AverageTime)
52  @OutputTimeUnit(TimeUnit.NANOSECONDS)
53  @Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
54  @Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
55  @State(Scope.Benchmark)
56  @Fork(value = 1, jvmArgs = { "-server", "-Xms128M", "-Xmx128M" })
57  public class BaselineGenerationPerformance {
58      /**
59       * The size of the array for testing {@link UniformRandomProvider#nextBytes(byte[])}.
60       *
61       * <p>This is a small prime number (127). This satisfies the following requirements:</p>
62       *
63       * <ul>
64       *   <li>The number of bytes will be allocated when testing so the allocation overhead
65       *   should be small.
66       *   <li>The number must be set so that filling the bytes from an {@code int} or {@code long}
67       *   source has no advantage for the 32-bit source, e.g. the same number of underlying bits have
68       *   to be generated. Note: 127 / 4 ~ 32 ints or 127 / 8 ~ 16 longs.
69       *   <li>The number should not be a factor of 4 to prevent filling completely using a 32-bit
70       *   source. This tests the edge case of partial fill.
71       * </ul>
72       */
73      static final int NEXT_BYTES_SIZE = 127;
74  
75      /**
76       * The upper limit for testing {@link UniformRandomProvider#nextInt(int)}.
77       *
78       * <p>This is the biggest prime number for an {@code int} (2147483629) to give a worst case
79       * run-time for the method.</p>
80       */
81      static final int NEXT_INT_LIMIT = 2_147_483_629;
82  
83      /**
84       * The upper limit for testing {@link UniformRandomProvider#nextLong(long)}.
85       *
86       * <p>This is the biggest prime number for a {@code long} (9223372036854775783L) to
87       * give a worst case run-time for the method.</p>
88       */
89      static final long NEXT_LONG_LIMIT = 9_223_372_036_854_775_783L;
90  
91      /**
92       * The provider for testing {@link UniformRandomProvider#nextByte()} and
93       * {@link UniformRandomProvider#nextByte(int)}.
94       */
95      private UniformRandomProvider nextBytesProvider = BaselineUtils.getNextBytes();
96  
97      /**
98       * The provider for testing {@link UniformRandomProvider#nextInt()} and
99       * {@link UniformRandomProvider#nextInt(int)}.
100      */
101     private UniformRandomProvider nextIntProvider = BaselineUtils.getNextInt();
102 
103     /**
104      * The provider for testing {@link UniformRandomProvider#nextLong()} and
105      * {@link UniformRandomProvider#nextLong(long)}.
106      */
107     private UniformRandomProvider nextLongProvider = BaselineUtils.getNextLong();
108 
109     /**
110      * The provider for testing {@link UniformRandomProvider#nextBoolean()}.
111      */
112     private UniformRandomProvider nextBooleanProvider = BaselineUtils.getNextBoolean();
113 
114     /**
115      * The provider for testing {@link UniformRandomProvider#nextFloat()}.
116      */
117     private UniformRandomProvider nextFloatProvider = BaselineUtils.getNextFloat();
118 
119     /**
120      * The provider for testing {@link UniformRandomProvider#nextDouble()}.
121      */
122     private UniformRandomProvider nextDoubleProvider = BaselineUtils.getNextDouble();
123 
124     /**
125      * Number of random values to generate when testing linearity. This must be high to avoid
126      * JIT optimisation of small loop constructs.
127      *
128      * <p>Note: Following the convention in the JMH Blackhole::consumCPU(long) method
129      * the loops are constructed to count down (although since there is no consumption
130      * of the loop counter the loop construct may be rewritten anyway).</p>
131      */
132     @Param({"50000", "100000", "150000", "200000", "250000"})
133     private int numValues;
134 
135     /**
136      * Exercise the {@link UniformRandomProvider#nextBytes(byte[])} method.
137      *
138      * <p>Note: Currently there is not a test for
139      * {@link UniformRandomProvider#nextBytes(byte[], int, int)} since the two methods are
140      * implemented by the base Int/LongProvider class using the same code.</p>
141      *
142      * @param bh Data sink.
143      */
144     @Benchmark
145     public void nextBytes(Blackhole bh) {
146         // The array allocation is not part of the benchmark.
147         final byte[] result = new byte[NEXT_BYTES_SIZE];
148         for (int i = numValues; i > 0; i--) {
149             nextBytesProvider.nextBytes(result);
150             bh.consume(result);
151         }
152     }
153 
154     /**
155      * Exercise the {@link UniformRandomProvider#nextInt()} method.
156      *
157      * @param bh Data sink.
158      */
159     @Benchmark
160     public void nextInt(Blackhole bh) {
161         for (int i = numValues; i > 0; i--) {
162             bh.consume(nextIntProvider.nextInt());
163         }
164     }
165 
166     /**
167      * Exercise the {@link UniformRandomProvider#nextInt(int)} method.
168      *
169      * @param bh Data sink.
170      */
171     @Benchmark
172     public void nextIntN(Blackhole bh) {
173         for (int i = numValues; i > 0; i--) {
174             bh.consume(nextIntProvider.nextInt(NEXT_INT_LIMIT));
175         }
176     }
177 
178     /**
179      * Exercise the {@link UniformRandomProvider#nextLong()} method.
180      *
181      * @param bh Data sink.
182      */
183     @Benchmark
184     public void nextLong(Blackhole bh) {
185         for (int i = numValues; i > 0; i--) {
186             bh.consume(nextLongProvider.nextLong());
187         }
188     }
189 
190     /**
191      * Exercise the {@link UniformRandomProvider#nextLong(long)} method.
192      *
193      * @param bh Data sink.
194      */
195     @Benchmark
196     public void nextLongN(Blackhole bh) {
197         for (int i = numValues; i > 0; i--) {
198             bh.consume(nextLongProvider.nextLong(NEXT_LONG_LIMIT));
199         }
200     }
201 
202     /**
203      * Exercise the {@link UniformRandomProvider#nextBoolean()} method.
204      *
205      * @param bh Data sink.
206      */
207     @Benchmark
208     public void nextBoolean(Blackhole bh) {
209         for (int i = numValues; i > 0; i--) {
210             bh.consume(nextBooleanProvider.nextBoolean());
211         }
212     }
213 
214     /**
215      * Exercise the {@link UniformRandomProvider#nextFloat()} method.
216      *
217      * @param bh Data sink.
218      */
219     @Benchmark
220     public void nextFloat(Blackhole bh) {
221         for (int i = numValues; i > 0; i--) {
222             bh.consume(nextFloatProvider.nextFloat());
223         }
224     }
225 
226     /**
227      * Exercise the {@link UniformRandomProvider#nextDouble()} method.
228      *
229      * @param bh Data sink.
230      */
231     @Benchmark
232     public void nextDouble(Blackhole bh) {
233         for (int i = numValues; i > 0; i--) {
234             bh.consume(nextDoubleProvider.nextDouble());
235         }
236     }
237 }