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.examples.jmh.RandomSources;
22  import org.apache.commons.rng.sampling.distribution.AliasMethodDiscreteSampler;
23  import org.apache.commons.rng.sampling.distribution.DiscreteSampler;
24  import org.apache.commons.rng.sampling.distribution.DiscreteUniformSampler;
25  import org.apache.commons.rng.sampling.distribution.GeometricSampler;
26  import org.apache.commons.rng.sampling.distribution.GuideTableDiscreteSampler;
27  import org.apache.commons.rng.sampling.distribution.LargeMeanPoissonSampler;
28  import org.apache.commons.rng.sampling.distribution.MarsagliaTsangWangDiscreteSampler;
29  import org.apache.commons.rng.sampling.distribution.RejectionInversionZipfSampler;
30  import org.apache.commons.rng.sampling.distribution.SmallMeanPoissonSampler;
31  
32  import org.openjdk.jmh.annotations.Benchmark;
33  import org.openjdk.jmh.annotations.BenchmarkMode;
34  import org.openjdk.jmh.annotations.Fork;
35  import org.openjdk.jmh.annotations.Measurement;
36  import org.openjdk.jmh.annotations.Mode;
37  import org.openjdk.jmh.annotations.OutputTimeUnit;
38  import org.openjdk.jmh.annotations.Param;
39  import org.openjdk.jmh.annotations.Scope;
40  import org.openjdk.jmh.annotations.Setup;
41  import org.openjdk.jmh.annotations.State;
42  import org.openjdk.jmh.annotations.Warmup;
43  
44  import java.util.concurrent.TimeUnit;
45  
46  /**
47   * Executes benchmark to compare the speed of generation of random numbers
48   * from the various source providers for different types of {@link DiscreteSampler}.
49   */
50  @BenchmarkMode(Mode.AverageTime)
51  @OutputTimeUnit(TimeUnit.NANOSECONDS)
52  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
53  @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
54  @State(Scope.Benchmark)
55  @Fork(value = 1, jvmArgs = {"-server", "-Xms128M", "-Xmx128M"})
56  public class DiscreteSamplersPerformance {
57      /**
58       * The value.
59       *
60       * <p>This must NOT be final!</p>
61       */
62      private int value;
63  
64      /**
65       * The {@link DiscreteSampler} samplers to use for testing. Creates the sampler for each
66       * {@link org.apache.commons.rng.simple.RandomSource RandomSource} in the default
67       * {@link RandomSources}.
68       */
69      @State(Scope.Benchmark)
70      public static class Sources extends RandomSources {
71          /** The probabilities for the discrete distribution. */
72          private static final double[] DISCRETE_PROBABILITIES;
73  
74          static {
75              // The size of this distribution will effect the relative performance
76              // of the AliasMethodDiscreteSampler against the other samplers. The
77              // Alias sampler is optimised for power of 2 tables and will zero pad
78              // the distribution. Pick a midpoint value between a power of 2 size to
79              // baseline half of a possible speed advantage.
80              final int size = (32 + 64) / 2;
81              DISCRETE_PROBABILITIES = new double[size];
82              for (int i = 0; i < size; i++) {
83                  DISCRETE_PROBABILITIES[i] = (i + 1.0) / size;
84              }
85          }
86  
87          /**
88           * The sampler type.
89           */
90          @Param({"DiscreteUniformSampler",
91                  "RejectionInversionZipfSampler",
92                  "SmallMeanPoissonSampler",
93                  "LargeMeanPoissonSampler",
94                  "GeometricSampler",
95                  "MarsagliaTsangWangDiscreteSampler",
96                  "MarsagliaTsangWangPoissonSampler",
97                  "MarsagliaTsangWangBinomialSampler",
98                  "GuideTableDiscreteSampler",
99                  "AliasMethodDiscreteSampler"})
100         private String samplerType;
101 
102         /** The sampler. */
103         private DiscreteSampler sampler;
104 
105         /**
106          * @return the sampler.
107          */
108         public DiscreteSampler getSampler() {
109             return sampler;
110         }
111 
112         /** Instantiates sampler. */
113         @Override
114         @Setup
115         public void setup() {
116             super.setup();
117             final UniformRandomProvider rng = getGenerator();
118             if ("DiscreteUniformSampler".equals(samplerType)) {
119                 sampler = DiscreteUniformSampler.of(rng, -98, 76);
120             } else if ("RejectionInversionZipfSampler".equals(samplerType)) {
121                 sampler = RejectionInversionZipfSampler.of(rng, 43, 2.1);
122             } else if ("SmallMeanPoissonSampler".equals(samplerType)) {
123                 sampler = SmallMeanPoissonSampler.of(rng, 8.9);
124             } else if ("LargeMeanPoissonSampler".equals(samplerType)) {
125                 // Note: Use with a fractional part to the mean includes a small mean sample
126                 sampler = LargeMeanPoissonSampler.of(rng, 41.7);
127             } else if ("GeometricSampler".equals(samplerType)) {
128                 sampler = GeometricSampler.of(rng, 0.21);
129             } else if ("MarsagliaTsangWangDiscreteSampler".equals(samplerType)) {
130                 sampler = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, DISCRETE_PROBABILITIES);
131             } else if ("MarsagliaTsangWangPoissonSampler".equals(samplerType)) {
132                 sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, 8.9);
133             } else if ("MarsagliaTsangWangBinomialSampler".equals(samplerType)) {
134                 sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, 20, 0.33);
135             } else if ("GuideTableDiscreteSampler".equals(samplerType)) {
136                 sampler = GuideTableDiscreteSampler.of(rng, DISCRETE_PROBABILITIES);
137             } else if ("AliasMethodDiscreteSampler".equals(samplerType)) {
138                 sampler = AliasMethodDiscreteSampler.of(rng, DISCRETE_PROBABILITIES);
139             }
140         }
141     }
142 
143     // Benchmarks methods below.
144 
145     /**
146      * Baseline for the JMH timing overhead for production of an {@code int} value.
147      *
148      * @return the {@code int} value
149      */
150     @Benchmark
151     public int baseline() {
152         return value;
153     }
154 
155     /**
156      * Run the sampler.
157      *
158      * @param sources Source of randomness.
159      * @return the sample value
160      */
161     @Benchmark
162     public int sample(Sources sources) {
163         return sources.getSampler().sample();
164     }
165 }