001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.rng.examples.jmh.core; 019 020import java.util.function.IntSupplier; 021import org.apache.commons.rng.UniformRandomProvider; 022import org.apache.commons.rng.core.source64.LongProvider; 023import org.apache.commons.rng.examples.jmh.RandomSources; 024import org.apache.commons.rng.simple.RandomSource; 025import org.openjdk.jmh.annotations.Benchmark; 026import org.openjdk.jmh.annotations.Param; 027import org.openjdk.jmh.annotations.Scope; 028import org.openjdk.jmh.annotations.Setup; 029import org.openjdk.jmh.annotations.State; 030 031/** 032 * Executes a benchmark to compare the speed of generation of random numbers from the 033 * various source providers using the bit cache verses simple generation. 034 */ 035public class CachedNextGenerationPerformance extends AbstractBenchmark { 036 /** The value. Must NOT be final to prevent JVM optimisation! */ 037 private boolean booleanValue; 038 /** The value. Must NOT be final to prevent JVM optimisation! */ 039 private int intValue; 040 041 /** 042 * Provides a function to obtain a boolean value from the various "RandomSource"s. 043 * It exercise the default nextBoolean() method (which may use 044 * a cache of bits) against a sign test on the native output. 045 */ 046 @State(Scope.Benchmark) 047 public static class BooleanSources extends RandomSources { 048 /** Functional interface for a boolean generator. */ 049 interface BooleanSupplier { 050 /** 051 * @return the boolean 052 */ 053 boolean getAsBoolean(); 054 } 055 056 /** 057 * The method to create the boolean value. 058 */ 059 @Param({"nextBoolean", "signTest"}) 060 private String method; 061 062 /** The generator. */ 063 private BooleanSupplier generator; 064 065 /** 066 * @return the next boolean 067 */ 068 boolean next() { 069 return generator.getAsBoolean(); 070 } 071 072 /** {@inheritDoc} */ 073 @Override 074 @Setup 075 public void setup() { 076 // Create the generator. 077 super.setup(); 078 final UniformRandomProvider rng = getGenerator(); 079 080 // Create the method to generate the boolean 081 if ("signTest".equals(method)) { 082 if (rng instanceof LongProvider) { 083 generator = () -> rng.nextLong() < 0; 084 } else { 085 // Assumed IntProvider 086 generator = () -> rng.nextInt() < 0; 087 } 088 } else if ("nextBoolean".equals(method)) { 089 // Do not use a method handle 'rng::nextBoolean' for the nextBoolean 090 // to attempt to maintain a comparable lambda function. The JVM may 091 // optimise this away. 092 generator = () -> rng.nextBoolean(); 093 } else { 094 throw new IllegalStateException("Unknown boolean method: " + method); 095 } 096 } 097 } 098 /** 099 * Provides a function to obtain an int value from the various "RandomSource"s 100 * that produce 64-bit output. 101 * It exercise the default nextInt() method (which may use 102 * a cache of bits) against a shift on the native output. 103 */ 104 @State(Scope.Benchmark) 105 public static class IntSources { 106 /** 107 * RNG providers. This list is maintained in the order of the {@link RandomSource} enum. 108 * 109 * <p>Include only those that are a {@link LongProvider}.</p> 110 */ 111 @Param({"SPLIT_MIX_64", 112 "XOR_SHIFT_1024_S", 113 "TWO_CMRES", 114 "MT_64", 115 "XOR_SHIFT_1024_S_PHI", 116 "XO_RO_SHI_RO_128_PLUS", 117 "XO_RO_SHI_RO_128_SS", 118 "XO_SHI_RO_256_PLUS", 119 "XO_SHI_RO_256_SS", 120 "XO_SHI_RO_512_PLUS", 121 "XO_SHI_RO_512_SS", 122 "PCG_RXS_M_XS_64", 123 "SFC_64", 124 "JSF_64", 125 "XO_RO_SHI_RO_128_PP", 126 "XO_SHI_RO_256_PP", 127 "XO_SHI_RO_512_PP", 128 "XO_RO_SHI_RO_1024_PP", 129 "XO_RO_SHI_RO_1024_S", 130 "XO_RO_SHI_RO_1024_SS", 131 "PCG_RXS_M_XS_64_OS"}) 132 private String randomSourceName; 133 134 /** 135 * The method to create the int value. 136 */ 137 @Param({"nextInt", "shiftLong"}) 138 private String method; 139 140 /** The generator. */ 141 private IntSupplier gen; 142 143 /** 144 * @return the next int 145 */ 146 int next() { 147 return gen.getAsInt(); 148 } 149 150 /** Create the int source. */ 151 @Setup 152 public void setup() { 153 final UniformRandomProvider rng = RandomSource.valueOf(randomSourceName).create(); 154 if (!(rng instanceof LongProvider)) { 155 throw new IllegalStateException("Not a LongProvider: " + rng.getClass().getName()); 156 } 157 158 // Create the method to generate the int 159 if ("shiftLong".equals(method)) { 160 gen = () -> (int) (rng.nextLong() >>> 32); 161 } else if ("nextInt".equals(method)) { 162 // Do not use a method handle 'rng::nextInt' for the nextInt 163 // to attempt to maintain a comparable lambda function. The JVM may 164 // optimise this away. 165 gen = () -> rng.nextInt(); 166 } else { 167 throw new IllegalStateException("Unknown int method: " + method); 168 } 169 } 170 } 171 172 /** 173 * Baseline for a JMH method call with no return value. 174 */ 175 @Benchmark 176 public void baselineVoid() { 177 // Do nothing, this is a baseline 178 } 179 180 /** 181 * Baseline for a JMH method call returning a {@code boolean}. 182 * 183 * @return the value 184 */ 185 @Benchmark 186 public boolean baselineBoolean() { 187 return booleanValue; 188 } 189 190 /** 191 * Baseline for a JMH method call returning an {@code int}. 192 * 193 * @return the value 194 */ 195 @Benchmark 196 public int baselineInt() { 197 return intValue; 198 } 199 200 /** 201 * Exercise the boolean generation method. 202 * 203 * @param sources Source of randomness. 204 * @return the boolean 205 */ 206 @Benchmark 207 public boolean nextBoolean(BooleanSources sources) { 208 return sources.next(); 209 } 210 211 /** 212 * Exercise the int generation method. 213 * 214 * @param sources Source of randomness. 215 * @return the int 216 */ 217 @Benchmark 218 public int nextInt(IntSources sources) { 219 return sources.next(); 220 } 221}