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.sampling.distribution;
19  
20  import org.apache.commons.rng.UniformRandomProvider;
21  import org.apache.commons.rng.sampling.distribution.DiscreteInverseCumulativeProbabilityFunction;
22  import org.apache.commons.rng.sampling.distribution.DiscreteSampler;
23  import org.apache.commons.rng.sampling.distribution.GeometricSampler;
24  import org.apache.commons.rng.sampling.distribution.InverseTransformDiscreteSampler;
25  import org.apache.commons.rng.simple.RandomSource;
26  import org.openjdk.jmh.annotations.Benchmark;
27  import org.openjdk.jmh.annotations.BenchmarkMode;
28  import org.openjdk.jmh.annotations.Fork;
29  import org.openjdk.jmh.annotations.Measurement;
30  import org.openjdk.jmh.annotations.Mode;
31  import org.openjdk.jmh.annotations.OutputTimeUnit;
32  import org.openjdk.jmh.annotations.Param;
33  import org.openjdk.jmh.annotations.Scope;
34  import org.openjdk.jmh.annotations.Setup;
35  import org.openjdk.jmh.annotations.State;
36  import org.openjdk.jmh.annotations.Warmup;
37  
38  import java.util.concurrent.TimeUnit;
39  
40  /**
41   * Executes a benchmark to compare the speed of generation of Geometric random numbers
42   * using different methods.
43   */
44  @BenchmarkMode(Mode.AverageTime)
45  @OutputTimeUnit(TimeUnit.NANOSECONDS)
46  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
47  @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
48  @State(Scope.Benchmark)
49  @Fork(value = 1, jvmArgs = {"-server", "-Xms128M", "-Xmx128M"})
50  public class GeometricSamplersPerformance {
51      /**
52       * The value.
53       *
54       * <p>This must NOT be final!</p>
55       */
56      private int value;
57  
58      /**
59       * The samplers's to use for testing. Defines the RandomSource, probability of success
60       * and the type of Geometric sampler.
61       */
62      @State(Scope.Benchmark)
63      public static class Sources {
64          /**
65           * RNG providers.
66           *
67           * <p>Use different speeds.</p>
68           *
69           * @see <a href="https://commons.apache.org/proper/commons-rng/userguide/rng.html">
70           *      Commons RNG user guide</a>
71           */
72          @Param({"SPLIT_MIX_64",
73                  "MWC_256",
74                  "JDK"})
75          private String randomSourceName;
76  
77          /**
78           * The probability of success.
79           */
80          @Param({"0.1", "0.3"})
81          private double probabilityOfSuccess;
82  
83          /**
84           * The sampler type.
85           */
86          @Param({"GeometricSampler", "InverseTransformDiscreteSampler"})
87          private String samplerType;
88  
89          /** The sampler. */
90          private DiscreteSampler sampler;
91  
92          /**
93           * @return the sampler.
94           */
95          public DiscreteSampler getSampler() {
96              return sampler;
97          }
98  
99          /** Instantiates sampler. */
100         @Setup
101         public void setup() {
102             final RandomSource randomSource = RandomSource.valueOf(randomSourceName);
103             final UniformRandomProvider rng = randomSource.create();
104             if ("GeometricSampler".equals(samplerType)) {
105                 sampler = GeometricSampler.of(rng, probabilityOfSuccess);
106             } else {
107                 final DiscreteInverseCumulativeProbabilityFunction geometricFunction =
108                     new GeometricDiscreteInverseCumulativeProbabilityFunction(probabilityOfSuccess);
109                 sampler = InverseTransformDiscreteSampler.of(rng, geometricFunction);
110             }
111         }
112     }
113 
114     /**
115      * Define the inverse cumulative probability function for the Geometric distribution.
116      *
117      * <p>Adapted from org.apache.commons.math3.distribution.GeometricDistribution.
118      */
119     private static class GeometricDiscreteInverseCumulativeProbabilityFunction
120             implements DiscreteInverseCumulativeProbabilityFunction {
121         /**
122          * {@code log(1 - p)} where p is the probability of success.
123          */
124         private final double log1mProbabilityOfSuccess;
125 
126         /**
127          * @param probabilityOfSuccess the probability of success
128          */
129         GeometricDiscreteInverseCumulativeProbabilityFunction(double probabilityOfSuccess) {
130             // No validation that 0 < p <= 1
131             log1mProbabilityOfSuccess = Math.log1p(-probabilityOfSuccess);
132         }
133 
134         @Override
135         public int inverseCumulativeProbability(double cumulativeProbability) {
136             // This is the equivalent of floor(log(u)/ln(1-p))
137             // where:
138             // u = cumulative probability
139             // p = probability of success
140             // See: https://en.wikipedia.org/wiki/Geometric_distribution#Related_distributions
141             // ---
142             // Note: if cumulativeProbability == 0 then log1p(-0) is zero and the result
143             // after the range check is 0.
144             // Note: if cumulativeProbability == 1 then log1p(-1) is negative infinity, the result
145             // of the divide is positive infinity and the result after the range check is
146             // Integer.MAX_VALUE.
147             return Math.max(0, (int) Math.ceil(Math.log1p(-cumulativeProbability) / log1mProbabilityOfSuccess - 1));
148         }
149     }
150 
151     /**
152      * Baseline for the JMH timing overhead for production of an {@code int} value.
153      *
154      * @return the {@code int} value
155      */
156     @Benchmark
157     public int baseline() {
158         return value;
159     }
160 
161     /**
162      * Run the sampler.
163      *
164      * @param sources Source of randomness.
165      * @return the sample value
166      */
167     @Benchmark
168     public int sample(Sources sources) {
169         return sources.getSampler().sample();
170     }
171 }