MXS-2247 Add support for random number generation to maxutils

Classes for xorshift and std random. Add a random number generator to Worker.
This commit is contained in:
Niclas Antti 2019-01-09 12:53:10 +02:00
parent 7cac2c009d
commit 9823fe2651
6 changed files with 228 additions and 15 deletions

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2019 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2022-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#pragma once
#include <maxbase/ccdefs.hh>
#include <random>
#include <array>
namespace maxbase
{
/**
* XorShiftRandom is an extremely fast all purpose random number generator.
*
* Uses xoshiro256** http://xoshiro.di.unimi.it written in 2018 by David Blackman
* and Sebastiano Vigna. Comment from the source code:
* This is xoshiro256** 1.0, our all-purpose, rock-solid generator. It has
* excellent (sub-ns) speed, a state (256 bits) that is large enough for
* any parallel application, and it passes all tests we are aware of.
*/
class XorShiftRandom
{
public:
// Non-deterministic if seed == 0
explicit XorShiftRandom(uint64_t seed = 0);
uint64_t rand();
uint32_t rand32();
bool rand_bool();
int64_t b_to_e_exclusive(int64_t b, int64_t e);
double zero_to_one_exclusive();
private:
uint64_t rotl(const uint64_t x, int k);
std::array<uint64_t, 4> m_state;
};
/**
* StdTwisterRandom is a class for random number generation using the C++ standard library.
* Uses the Mersenne Twister algorithms (mt19937 and mt19937_64).
*/
class StdTwisterRandom
{
public:
// Non-deterministic if seed == 0
explicit StdTwisterRandom(uint64_t seed = 0);
uint64_t rand();
uint32_t rand32();
bool rand_bool();
int64_t b_to_e_exclusive(int64_t b, int64_t e);
double zero_to_one_exclusive();
// Borrow the mt19937_64 engine for other distributions.
std::mt19937_64& rnd_engine();
private:
std::mt19937 m_twister_engine_32;
std::mt19937_64 m_twister_engine_64;
};
// *********************************
// inlined functions below this line
inline uint64_t XorShiftRandom::rotl(const uint64_t x, int k)
{
return (x << k) | (x >> (64 - k));
}
inline uint64_t XorShiftRandom::rand()
{
// xoshiro256**
const uint64_t result_starstar = rotl(m_state[1] * 5, 7) * 9;
const uint64_t t = m_state[1] << 17;
m_state[2] ^= m_state[0];
m_state[3] ^= m_state[1];
m_state[1] ^= m_state[2];
m_state[0] ^= m_state[3];
m_state[2] ^= t;
m_state[3] = rotl(m_state[3], 45);
return result_starstar;
}
inline uint32_t XorShiftRandom::rand32()
{
return rand() >> 32; // shifting, although low bits are good
}
inline bool XorShiftRandom::rand_bool()
{
return std::signbit(int64_t(rand()));
}
inline double XorShiftRandom::zero_to_one_exclusive()
{
uint64_t x = rand();
// Turn uint64_t to a [0,1[ double (yes, it's standard and portable)
const union
{
uint64_t i;
double d;
} u = {.i = UINT64_C(0x3FF) << 52 | x >> 12};
return u.d - 1.0;
}
inline int64_t XorShiftRandom::b_to_e_exclusive(int64_t b, int64_t e)
{
// With 64 bits mod bias does not happen in practise (a very, very large e-b would be needed).
// alternative: return b + int64_t(zero_to_one_exclusive()*(e-b));
return b + rand() % (e - b);
}
inline uint64_t StdTwisterRandom::rand()
{
return m_twister_engine_64();
}
inline uint32_t StdTwisterRandom::rand32()
{
return m_twister_engine_32();
}
inline bool StdTwisterRandom::rand_bool()
{
return std::signbit(int32_t(rand32()));
}
inline double StdTwisterRandom::zero_to_one_exclusive()
{
std::uniform_real_distribution<double> zero_to_one {0, 1};
return zero_to_one(m_twister_engine_64);
}
inline int64_t StdTwisterRandom::b_to_e_exclusive(int64_t b, int64_t e)
{
std::uniform_int_distribution<int64_t> dist {b, e - 1};
return dist(m_twister_engine_64);
}
}

View File

@ -29,6 +29,7 @@
#include <maxbase/semaphore.hh>
#include <maxbase/worker.h>
#include <maxbase/workertask.hh>
#include <maxbase/random.hh>
namespace maxbase
{
@ -248,11 +249,12 @@ class Worker : public MXB_WORKER
Worker& operator=(const Worker&) = delete;
public:
typedef WORKER_STATISTICS STATISTICS;
typedef WorkerTask Task;
typedef WorkerDisposableTask DisposableTask;
typedef WorkerLoad Load;
typedef WorkerTimer Timer;
using STATISTICS = WORKER_STATISTICS;
using Task = WorkerTask;
using DisposableTask = WorkerDisposableTask;
using Load = WorkerLoad;
using Timer = WorkerTimer;
using RandomEngine = maxbase::XorShiftRandom;
/**
* A delegating timer that delegates the timer tick handling
@ -369,6 +371,11 @@ public:
*/
void get_descriptor_counts(uint32_t* pnCurrent, uint64_t* pnTotal);
/**
* Return the random engine of this worker.
*/
RandomEngine& random_engine();
/**
* Add a file descriptor to the epoll instance of the worker.
*
@ -958,6 +965,7 @@ private:
PrivateTimer* m_pTimer; /*< The worker's own timer. */
DelayedCallsByTime m_sorted_calls; /*< Current delayed calls sorted by time. */
DelayedCallsById m_calls; /*< Current delayed calls indexed by id. */
RandomEngine m_random_engine; /*< Random engine for this worker (this thread). */
int32_t m_next_delayed_call_id; /*< The next delayed call id. */
};

View File

@ -15,6 +15,7 @@ add_library(maxbase STATIC
worker.cc
workertask.cc
average.cc
random.cc
)
if(HAVE_SYSTEMD)

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2019 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2022-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxbase/random.hh>
namespace maxbase
{
static uint64_t splitmix(uint64_t& state)
{
uint64_t z = (state += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
XorShiftRandom::XorShiftRandom(uint64_t seed)
{
if (!seed)
{
std::random_device rdev;
while (!(seed = rdev()))
{
}
}
for (auto& s : m_state)
{
s = splitmix(seed);
}
}
StdTwisterRandom::StdTwisterRandom(uint64_t seed)
{
std::random_device rdev;
m_twister_engine_32.seed(seed ? seed : rdev());
m_twister_engine_64.seed(seed ? seed : rdev());
}
std::mt19937_64& StdTwisterRandom::rnd_engine()
{
return m_twister_engine_64;
}
}

View File

@ -355,6 +355,11 @@ void Worker::get_descriptor_counts(uint32_t* pnCurrent, uint64_t* pnTotal)
*pnTotal = atomic_load_uint64(&m_nTotal_descriptors);
}
Worker::RandomEngine& Worker::random_engine()
{
return m_random_engine;
}
bool Worker::add_fd(int fd, uint32_t events, MXB_POLL_DATA* pData)
{
bool rv = true;

View File

@ -28,15 +28,6 @@
using namespace maxscale;
// TODO, there should be a utility with the most common used random cases.
// FYI: rand() is about twice as fast as the below toss.
static std::mt19937 random_engine;
static std::uniform_real_distribution<> zero_to_one(0.0, 1.0);
double toss()
{
return zero_to_one(random_engine);
}
/**
* The functions that implement back end selection for the read write
* split router. All of these functions are internal to that router and
@ -161,7 +152,8 @@ PRWBackends::iterator backend_cmp_response_time(PRWBackends& sBackends)
}
// Find the winner, role the ball:
double ball = toss();
double ball = maxbase::Worker::get_current()->random_engine().zero_to_one_exclusive();
double slot_walk {0};
int winner {0};