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:
parent
7cac2c009d
commit
9823fe2651
154
maxutils/maxbase/include/maxbase/random.hh
Normal file
154
maxutils/maxbase/include/maxbase/random.hh
Normal 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);
|
||||
}
|
||||
}
|
@ -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. */
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ add_library(maxbase STATIC
|
||||
worker.cc
|
||||
workertask.cc
|
||||
average.cc
|
||||
random.cc
|
||||
)
|
||||
|
||||
if(HAVE_SYSTEMD)
|
||||
|
53
maxutils/maxbase/src/random.cc
Normal file
53
maxutils/maxbase/src/random.cc
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user