 
                        
        
                        
        scala.concurrent.stm.skel.SimpleRandom.scala Maven / Gradle / Ivy
/* scala-stm - (c) 2009-2011, Stanford University, PPL */
package scala.concurrent.stm.skel
/** A random number generator that focuses on speed and lack of inter-thread
 *  interference, rather than on the quality of the numbers returned.  The
 *  `object SimpleRandom` is striped internally to reduce
 *  contention when accessed from multiple threads.  The `class
 *  SimpleRandom` should only be used by a single thread.
 *  
 *  The constants in this 64-bit linear congruential random number generator
 *  are from http://nuclear.llnl.gov/CNP/rng/rngman/node4.html.
 *
 *  @author Nathan Bronson
 */
object SimpleRandom {
  // 64 byte cache lines are typical, so there are 8 slots per cache line.
  // This means that the probability that any two threads have false sharing is
  // p = 8 / #slots.  If there are n processors, each of which is running 1
  // thread, then the probability that no other threads have false sharing with
  // the current thread is (1-p)^(n-1).  If p is small, that is about
  // 1 - (n-1)p, which is pretty close to 1 - np.  If we want the probability
  // of false conflict for a thread to be less than k, then we need np < k, or
  // p < k/n, or 8/Slots < k/n, or #slots > 8n/k.  If we let k = 1/8, then we
  // get #slots=64*n.
  private val mask = {
    val min = 64 * Runtime.getRuntime.availableProcessors
    var slots = 1
    while (slots < min) slots *= 2
    slots - 1
  }
  
  private val states = Array.tabulate(mask + 1)({ _ * 0x123456789abcdefL })
  /** Returns a random value chosen from a uniform distribution of all valid
   *  `Int`s.
   */
  def nextInt(): Int = {
    val id = (Thread.currentThread.getId.asInstanceOf[Int] * 13) & mask
    val next = step(states(id))
    states(id) = next
    extract(next)
  }
  
  /** Returns a random value chosen from a uniform distribution of the
   *  non-negative integers less than `n`, or throws `IllegalArgumentException`
   *  if `n` is negative or zero.
   */
  def nextInt(n: Int): Int = {
    if (n <= 0)
      throw new IllegalArgumentException
    var x = -1
    while (x == -1) x = tryClamp(nextInt(), n)
    x
  }
  private def step(x: Long) = x * 2862933555777941757L + 3037000493L
  
  private def extract(x: Long) = (x >> 30).asInstanceOf[Int]
  /** r is the random, returns -1 on failure. */
  private def tryClamp(r: Int, n: Int): Int = {
    // get a positive number
    val x = r & Int.MaxValue
    if ((n & -n) == n) {
      // for powers of two, we use high bits instead of low bits
      ((x.toLong * n) >> 31).toInt
    } else {
      val z = x % n
      if (x - z + (n - 1) < 0) {
        // x is bigger than floor(MAX_INT/n)*n, so we are not getting an even
        // distribution.  Try again.
        -1
      } else {
        z
      }
    }
  }
}
/** An clonable unsynchronized random number generator that uses the same
 *  algorithm as the concurrent `object SimpleRandom`.  The caller must ensure
 *  that each `SimpleRandom` instance is used from only one thread at a time.
 *
 *  @author Nathan Bronson
 */
class SimpleRandom private (private var _state: Long, dummy: Boolean) {
  import SimpleRandom._
  def this(seed: Int) = this(SimpleRandom.step(SimpleRandom.step(seed)), false)
  def this() = this(System.identityHashCode(Thread.currentThread))
  override def clone = new SimpleRandom(_state, false)
  /** Returns a random value chosen from a uniform distribution of all valid
   *  `Int`s.
   */
  def nextInt(): Int = {
    _state = step(_state)
    extract(_state)
  }
  /** Returns a random value chosen from a uniform distribution of the
   *  non-negative integers less than `n`, or throws `IllegalArgumentException`
   *  if `n` is negative or zero.
   */
  def nextInt(n: Int): Int = {
    if (n <= 0)
      throw new IllegalArgumentException
    var x = -1
    while (x == -1) x = tryClamp(nextInt(), n)
    x
  }
}