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:
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/semaphore.hh>
|
||||||
#include <maxbase/worker.h>
|
#include <maxbase/worker.h>
|
||||||
#include <maxbase/workertask.hh>
|
#include <maxbase/workertask.hh>
|
||||||
|
#include <maxbase/random.hh>
|
||||||
|
|
||||||
namespace maxbase
|
namespace maxbase
|
||||||
{
|
{
|
||||||
@ -248,11 +249,12 @@ class Worker : public MXB_WORKER
|
|||||||
Worker& operator=(const Worker&) = delete;
|
Worker& operator=(const Worker&) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef WORKER_STATISTICS STATISTICS;
|
using STATISTICS = WORKER_STATISTICS;
|
||||||
typedef WorkerTask Task;
|
using Task = WorkerTask;
|
||||||
typedef WorkerDisposableTask DisposableTask;
|
using DisposableTask = WorkerDisposableTask;
|
||||||
typedef WorkerLoad Load;
|
using Load = WorkerLoad;
|
||||||
typedef WorkerTimer Timer;
|
using Timer = WorkerTimer;
|
||||||
|
using RandomEngine = maxbase::XorShiftRandom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A delegating timer that delegates the timer tick handling
|
* A delegating timer that delegates the timer tick handling
|
||||||
@ -369,6 +371,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void get_descriptor_counts(uint32_t* pnCurrent, uint64_t* pnTotal);
|
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.
|
* Add a file descriptor to the epoll instance of the worker.
|
||||||
*
|
*
|
||||||
@ -958,6 +965,7 @@ private:
|
|||||||
PrivateTimer* m_pTimer; /*< The worker's own timer. */
|
PrivateTimer* m_pTimer; /*< The worker's own timer. */
|
||||||
DelayedCallsByTime m_sorted_calls; /*< Current delayed calls sorted by time. */
|
DelayedCallsByTime m_sorted_calls; /*< Current delayed calls sorted by time. */
|
||||||
DelayedCallsById m_calls; /*< Current delayed calls indexed by id. */
|
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. */
|
int32_t m_next_delayed_call_id; /*< The next delayed call id. */
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,7 @@ add_library(maxbase STATIC
|
|||||||
worker.cc
|
worker.cc
|
||||||
workertask.cc
|
workertask.cc
|
||||||
average.cc
|
average.cc
|
||||||
|
random.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
if(HAVE_SYSTEMD)
|
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);
|
*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 Worker::add_fd(int fd, uint32_t events, MXB_POLL_DATA* pData)
|
||||||
{
|
{
|
||||||
bool rv = true;
|
bool rv = true;
|
||||||
|
@ -28,15 +28,6 @@
|
|||||||
|
|
||||||
using namespace maxscale;
|
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
|
* The functions that implement back end selection for the read write
|
||||||
* split router. All of these functions are internal to that router and
|
* 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:
|
// 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};
|
double slot_walk {0};
|
||||||
int winner {0};
|
int winner {0};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user