base: Use STL C++11 random number generation
authorAndreas Hansson <andreas.hansson@arm.com>
Wed, 3 Sep 2014 11:42:55 +0000 (07:42 -0400)
committerAndreas Hansson <andreas.hansson@arm.com>
Wed, 3 Sep 2014 11:42:55 +0000 (07:42 -0400)
This patch changes the random number generator from the in-house
Mersenne twister to an implementation relying entirely on C++11 STL.

The format for the checkpointing of the twister is simplified. As the
functionality was never used this should not matter. Note that this
patch does not actually make use of the checkpointing
functionality. As the random number generator is not thread safe, it
may be sensible to create one generator per thread, system, or even
object. Until this is decided the status quo is maintained in that no
generator state is part of the checkpoint.

src/base/SConscript
src/base/random.cc
src/base/random.hh
src/base/random_mt.cc [deleted file]

index 0bb0cb0fb046a7d64d243ccb99e593e50e21c1a1..e7c420f78e699172caad6c0a4ff1a7ae37c2f91c 100644 (file)
@@ -51,7 +51,6 @@ Source('misc.cc')
 Source('output.cc')
 Source('pollevent.cc')
 Source('random.cc')
-Source('random_mt.cc')
 if env['TARGET_ISA'] != 'null':
     Source('remote_gdb.cc')
 Source('socket.cc')
index cffeddec95927d049d1f19a83b88ae813b21edaa..ced9a8f45b54ab25324562d1cc0f5adf36afb13b 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2003-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
  *
  * Authors: Nathan Binkert
  *          Ali Saidi
+ *          Andreas Hansson
  */
 
-#include <limits>
-#include "base/fenv.hh"
-#include "base/intmath.hh"
+#include <sstream>
+
 #include "base/misc.hh"
 #include "base/random.hh"
 #include "sim/serialize.hh"
 
-using namespace std;
-
 Random::Random()
 {
-    // default random seed taken from original source
+    // default random seed
     init(5489);
 }
 
@@ -49,79 +59,41 @@ Random::Random(uint32_t s)
     init(s);
 }
 
-Random::Random(uint32_t init_key[], int key_length)
-{
-    init(init_key, key_length);
-}
-
 Random::~Random()
 {
 }
 
-// To preserve the uniform random distribution between min and max,
-// and allow all numbers to be represented, we generate a uniform
-// random number to the nearest power of two greater than max.  If
-// this number doesn't fall between 0 and max, we try again.  Anything
-// else would skew the distribution.
-uint32_t
-Random::genrand(uint32_t max)
-{
-    if (max == 0)
-        return 0;
-    if (max == std::numeric_limits<uint32_t>::max())
-        return genrand();
-
-    int log = ceilLog2(max + 1);
-    int shift = (sizeof(uint32_t) * 8 - log);
-    uint32_t random;
-
-    do {
-        random = genrand() >> shift;
-    } while (random > max);
-
-    return random;
-}
-
-uint64_t
-Random::genrand(uint64_t max)
+void
+Random::init(uint32_t s)
 {
-    if (max == 0)
-        return 0;
-    if (max == std::numeric_limits<uint64_t>::max())
-        return genrand();
-
-    int log = ceilLog2(max + 1);
-    int shift = (sizeof(uint64_t) * 8 - log);
-    uint64_t random;
-
-    do {
-        random = (uint64_t)genrand() << 32 | (uint64_t)genrand();
-        random = random >> shift;
-    } while (random > max);
-
-    return random;
+    gen.seed(s);
 }
 
 void
-Random::serialize(const string &base, ostream &os)
+Random::serialize(std::ostream &os)
 {
-    int length = N;
-    paramOut(os, base + ".mti", mti);
-    paramOut(os, base + ".length", length);
-    arrayParamOut(os, base + ".data", mt, length);
+    panic("Currently not used anywhere.\n");
+
+    // get the state from the generator
+    std::ostringstream oss;
+    oss << gen;
+    std::string state = oss.str();
+    paramOut(os, "mt_state", state);
 }
 
 void
-Random::unserialize(const string &base, Checkpoint *cp, const string &section)
+Random::unserialize(Checkpoint *cp, const std::string &section)
 {
-    int length;
-
-    paramIn(cp, section, base + ".mti", mti);
-    paramIn(cp, section, base + ".length", length);
-    if (length != N)
-        panic("cant unserialize random number data. length != %d\n", length);
-
-    arrayParamIn(cp, section, base + ".data", mt, length);
+    panic("Currently not used anywhere.\n");
+
+    // the random generator state did not use to be part of the
+    // checkpoint state, so be forgiving in the unserialization and
+    // keep on going if the parameter is not there
+    std::string state;
+    if (optParamIn(cp, section, "mt_state", state)) {
+        std::istringstream iss(state);
+        iss >> gen;
+    }
 }
 
 Random random_mt;
index 34107c76f322a9ff1dc180316f78156146e8fb69..cedbd6bd44bcd8d581e78243020c28d6bdb162f1 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2003-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
  *
  * Authors: Nathan Binkert
  *          Ali Saidi
+ *          Andreas Hansson
  */
 
 /*
- * Mersenne Twister random number generator has a period of
- * 2^19937-1.
- *
- * The actual math is in its own file to keep the license clear.
+ * Mersenne twister random number generator.
  */
 
 #ifndef __BASE_RANDOM_HH__
 #define __BASE_RANDOM_HH__
 
-#include <ios>
+#include <random>
 #include <string>
+#include <type_traits>
 
 #include "base/types.hh"
 
@@ -48,192 +59,51 @@ class Checkpoint;
 
 class Random
 {
-  protected:
-    static const int N = 624;
-    static const int M = 397;
-    static const uint32_t MATRIX_A = (uint32_t)0x9908b0df;
-    static const uint32_t UPPER_MASK = (uint32_t)0x80000000;
-    static const uint32_t LOWER_MASK = (uint32_t)0x7fffffff;
-
-    uint32_t mt[N];
-    int mti;
-
-    uint32_t genrand();
-    uint32_t genrand(uint32_t max);
-    uint64_t genrand(uint64_t max);
-
-    void
-    _random(int8_t &value)
-    {
-        value = genrand() & (int8_t)-1;
-    }
-
-    void
-    _random(int16_t &value)
-    {
-        value = genrand() & (int16_t)-1;
-    }
-
-    void
-    _random(int32_t &value)
-    {
-        value = (int32_t)genrand();
-    }
-
-    void
-    _random(int64_t &value)
-    {
-        value = (int64_t)genrand() << 32 | (int64_t)genrand();
-    }
-
-    void
-    _random(uint8_t &value)
-    {
-        value = genrand() & (uint8_t)-1;
-    }
 
-    void
-    _random(uint16_t &value)
-    {
-        value = genrand() & (uint16_t)-1;
-    }
-
-    void
-    _random(uint32_t &value)
-    {
-        value = genrand();
-    }
+  private:
 
-    void
-    _random(uint64_t &value)
-    {
-        value = (uint64_t)genrand() << 32 | (uint64_t)genrand();
-    }
-
-    // [0,1]
-    void
-    _random(float &value)
-    {
-        // ieee floats have 23 bits of mantissa
-        value = (genrand() >> 9) / 8388608.0;
-    }
-
-    // [0,1]
-    void
-    _random(double &value)
-    {
-        double number = genrand() * 2097152.0 + (genrand() >> 11);
-        value = number / 9007199254740992.0;
-    }
-
-
-    // Range based versions of the random number generator
-    int8_t
-    _random(int8_t min, int8_t max)
-    {
-        uint32_t diff = max - min;
-        return static_cast<int8_t>(min + genrand(diff));
-    }
-
-    int16_t
-    _random(int16_t min, int16_t max)
-    {
-        uint32_t diff = max - min;
-        return static_cast<int16_t>(min + genrand(diff));
-    }
-
-    int32_t
-    _random(int32_t min, int32_t max)
-    {
-        uint32_t diff = max - min;
-        return static_cast<int32_t>(min + genrand(diff));
-    }
-
-    int64_t
-    _random(int64_t min, int64_t max)
-    {
-        uint64_t diff = max - min;
-        return static_cast<int64_t>(min + genrand(diff));
-    }
-
-    uint8_t
-    _random(uint8_t min, uint8_t max)
-    {
-        uint32_t diff = max - min;
-        return static_cast<uint8_t>(min + genrand(diff));
-    }
-
-    uint16_t
-    _random(uint16_t min, uint16_t max)
-    {
-        uint32_t diff = max - min;
-        return static_cast<uint16_t>(min + genrand(diff));
-    }
-
-    uint32_t
-    _random(uint32_t min, uint32_t max)
-    {
-        uint32_t diff = max - min;
-        return static_cast<uint32_t>(min + genrand(diff));
-    }
-
-    uint64_t
-    _random(uint64_t min, uint64_t max)
-    {
-        uint64_t diff = max - min;
-        return static_cast<uint64_t>(min + genrand(diff));
-    }
+    std::mt19937_64 gen;
 
   public:
+
     Random();
     Random(uint32_t s);
-    Random(uint32_t init_key[], int key_length);
     ~Random();
 
     void init(uint32_t s);
-    void init(uint32_t init_key[], int key_length);
 
+    /**
+     * Use the SFINAE idiom to choose an implementation based on
+     * whether the type is integral or floating point.
+     */
     template <typename T>
-    T
+    typename std::enable_if<std::is_integral<T>::value, T>::type
     random()
     {
-        T value;
-        _random(value);
-        return value;
+        // [0, max_value] for integer types
+        std::uniform_int_distribution<T> dist;
+        return dist(gen);
     }
 
     template <typename T>
-    T
-    random(T min, T max)
-    {
-        return _random(min, max);
-    }
-
-    // [0,1]
-    double
-    gen_real1()
-    {
-        return genrand() / 4294967296.0;
-    }
-
-    // [0,1)
-    double
-    gen_real2()
+    typename std::enable_if<std::is_floating_point<T>::value, T>::type
+    random()
     {
-        return genrand() / 4294967295.0;
+        // [0, 1) for real types
+        std::uniform_real_distribution<T> dist;
+        return dist(gen);
     }
 
-    // (0,1)
-    double
-    gen_real3()
+    template <typename T>
+    typename std::enable_if<std::is_integral<T>::value, T>::type
+    random(T min, T max)
     {
-        return ((double)genrand() + 0.5) / 4294967296.0;
+        std::uniform_int_distribution<T> dist(min, max);
+        return dist(gen);
     }
 
-  public:
-    void serialize(const std::string &base, std::ostream &os);
-    void unserialize(const std::string &base, Checkpoint *cp,
-                     const std::string &section);
+    void serialize(std::ostream &os);
+    void unserialize(Checkpoint *cp, const std::string &section);
 };
 
 extern Random random_mt;
diff --git a/src/base/random_mt.cc b/src/base/random_mt.cc
deleted file mode 100644 (file)
index 70a7b04..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * A C-program for MT19937, with initialization improved 2002/1/26.
- * Coded by Takuji Nishimura and Makoto Matsumoto.
- *
- * Before using, initialize the state by using init_genrand(seed)
- * or init_by_array(init_key, key_length).
- *
- * Copyright (C) 1997-2002 Makoto Matsumoto and Takuji Nishimura
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *   1. Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the following
- *   disclaimer in the documentation and/or other materials provided
- *   with the distribution.
- *
- *   3. The names of its contributors may not be used to endorse or
- *   promote products derived from this software without specific
- *   prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *
- * Any feedback is very welcome.
- * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
- * email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
- */
-
-#include "base/random.hh"
-
-/* initializes mt[N] with a seed */
-void
-Random::init(uint32_t s)
-{
-    mti = N + 1;
-    mt[0] = s & (uint32_t)0xffffffff;
-
-    for (mti = 1; mti < N; mti++) {
-        mt[mti] = 1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti;
-        /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
-        /* In the previous versions, MSBs of the seed affect   */
-        /* only MSBs of the array mt[].                        */
-        /* 2002/01/09 modified by Makoto Matsumoto             */
-        mt[mti] &= (uint32_t)0xffffffff;
-        /* for >32 bit machines */
-    }
-}
-
-/* initialize by an array with array-length */
-/* init_key is the array for initializing keys */
-/* key_length is its length */
-/* slight change for C++, 2004/2/26 */
-void
-Random::init(uint32_t init_key[], int key_length)
-{
-    int i = 1;
-    int j = 0;
-    int k = (N > key_length) ? N : key_length;
-
-    init(19650218);
-
-    for (; k; k--) {
-        mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * (uint32_t)1664525))
-          + init_key[j] + j; /* non linear */
-
-        mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
-
-        i++;
-        j++;
-
-        if (i >= N) {
-            mt[0] = mt[N - 1];
-            i = 1;
-        }
-
-        if (j >= key_length)
-            j = 0;
-    }
-
-    for (k = N - 1; k; k--) {
-        /* non linear */
-        mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1566083941UL)) - i;
-
-        /* for WORDSIZE > 32 machines */
-        mt[i] &= (uint32_t)0xffffffff;
-        i++;
-
-        if (i >= N) {
-            mt[0] = mt[N - 1];
-            i = 1;
-        }
-    }
-
-    /* MSB is 1; assuring non-zero initial array */
-    mt[0] = (uint32_t)0x80000000;
-}
-
-/* generates a random number on [0,0xffffffff]-interval */
-uint32_t
-Random::genrand()
-{
-    uint32_t y;
-    static uint32_t mag01[2] = { 0, MATRIX_A};
-
-    if (mti >= N) { /* generate N words at one time */
-        int kk;
-
-        for (kk = 0; kk < N - M; kk++) {
-            y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
-            mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
-        }
-        for (; kk < N - 1; kk++) {
-            y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
-            mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
-        }
-
-        y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
-        mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
-
-        mti = 0;
-    }
-
-    y = mt[mti++];
-
-    /* Tempering */
-    y ^= (y >> 11);
-    y ^= (y << 7) & (uint32_t)0x9d2c5680;
-    y ^= (y << 15) & (uint32_t)0xefc60000;
-    y ^= (y >> 18);
-
-    return y;
-}