Skip to content

Commit

Permalink
common: implement random number generator (following N3551)
Browse files Browse the repository at this point in the history
Adds new mini-library for random number generation using C++11's
<random> header.

Refer-to:
https://isocpp.org/files/papers/n3551.pdf

Signed-off-by: Jesse Williamson <[email protected]>
  • Loading branch information
Jesse Williamson committed Aug 22, 2017
1 parent af33c8b commit 8ac5472
Show file tree
Hide file tree
Showing 3 changed files with 478 additions and 0 deletions.
288 changes: 288 additions & 0 deletions src/include/random.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2017 SUSE LINUX GmbH
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*
*/

#ifndef CEPH_RANDOM_H
#define CEPH_RANDOM_H 1

#include <mutex>
#include <random>

#include "boost/optional.hpp"

#include "common/backport14.h"

// Basic random number facility, adapted from N3551:
namespace ceph {
namespace util {

inline namespace version_1_0 {

namespace detail {

template <typename EngineT>
EngineT& engine();

template <typename MutexT, typename EngineT>
void randomize_rng(const int seed, MutexT& m, EngineT& e)
{
std::lock_guard<MutexT> lg(m);
e.seed(seed);
}

template <typename MutexT, typename EngineT>
void randomize_rng(MutexT& m, EngineT& e)
{
thread_local std::random_device rd;

std::lock_guard<MutexT> lg(m);
e.seed(rd());
}

template <typename EngineT = std::default_random_engine>
void randomize_rng(const int n)
{
detail::engine<EngineT>().seed(n);
}

template <typename EngineT = std::default_random_engine>
void randomize_rng()
{
thread_local std::random_device rd;
detail::engine<EngineT>().seed(rd());
}

template <typename EngineT>
EngineT& engine()
{
thread_local boost::optional<EngineT> rng_engine;

if (!rng_engine) {
rng_engine.emplace(EngineT());
randomize_rng<EngineT>();
}

return *rng_engine;
}

} // namespace detail

namespace detail {

template <typename NumberT,
typename DistributionT,
typename EngineT>
NumberT generate_random_number(const NumberT min, const NumberT max,
EngineT& e)
{
thread_local DistributionT d { min, max };

using param_type = typename DistributionT::param_type;
return d(e, param_type { min, max });
}

template <typename NumberT,
typename MutexT,
typename DistributionT,
typename EngineT>
NumberT generate_random_number(const NumberT min, const NumberT max,
MutexT& m, EngineT& e)
{
thread_local DistributionT d { min, max };

using param_type = typename DistributionT::param_type;

std::lock_guard<MutexT> lg(m);
return d(e, param_type { min, max });
}

template <typename NumberT,
typename DistributionT,
typename EngineT>
NumberT generate_random_number(const NumberT min, const NumberT max)
{
return detail::generate_random_number<NumberT, DistributionT, EngineT>
(min, max, detail::engine<EngineT>());
}

template <typename MutexT, typename EngineT,
int min = 0,
int max = std::numeric_limits<int>::max(),
typename DistributionT = std::uniform_int_distribution<int>>
int generate_random_number(MutexT& m, EngineT& e)
{
return detail::generate_random_number<int, MutexT, DistributionT, EngineT>
(min, max, m, e);
}

} // namespace detail

template <typename EngineT = std::default_random_engine>
void randomize_rng()
{
detail::randomize_rng<EngineT>();
}

template <int min = 0,
int max = std::numeric_limits<int>::max(),
typename DistributionT = std::uniform_int_distribution<int>,
typename EngineT = std::default_random_engine>
int generate_random_number()
{
return detail::generate_random_number<int, DistributionT, EngineT>
(min, max);
}

template <typename IntegerT>
IntegerT generate_random_number(const IntegerT min, const IntegerT max,
ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
{
return detail::generate_random_number<IntegerT,
std::uniform_int_distribution<IntegerT>,
std::default_random_engine>
(min, max);
}

namespace detail {

template <typename IntegerT, typename MutexT, typename EngineT>
int generate_random_number(const IntegerT min, const IntegerT max,
MutexT& m, EngineT& e,
ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
{
return detail::generate_random_number<IntegerT, MutexT,
std::uniform_int_distribution<IntegerT>,
EngineT>
(min, max, m, e);
}

template <typename IntegerT, typename MutexT, typename EngineT>
int generate_random_number(const IntegerT max,
MutexT& m, EngineT& e,
ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
{
constexpr IntegerT zero = 0;
return generate_random_number(zero, max, m, e);
}

} // namespace detail

template <typename IntegerT>
int generate_random_number(const IntegerT max,
ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
{
constexpr IntegerT zero = 0;
return generate_random_number(zero, max);
}

template <typename RealT>
RealT generate_random_number(const RealT min, const RealT max,
ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
{
return detail::generate_random_number<RealT,
std::uniform_real_distribution<RealT>,
std::default_random_engine>
(min, max);
}

namespace detail {

template <typename RealT, typename MutexT>
RealT generate_random_number(const RealT max, MutexT& m,
ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
{
constexpr RealT zero = 0.0;
return generate_random_number(zero, max, m);
}

template <typename RealT, typename MutexT, typename EngineT>
RealT generate_random_number(const RealT min, const RealT max, MutexT& m, EngineT& e,
ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
{
return detail::generate_random_number<RealT, MutexT,
std::uniform_real_distribution<RealT>,
EngineT>
(min, max, m, e);
}


template <typename RealT, typename MutexT, typename EngineT>
RealT generate_random_number(const RealT max, MutexT& m, EngineT& e,
ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
{
constexpr RealT zero = 0.0;
return generate_random_number(zero, max, m, e);
}

} // namespace detail

template <typename RealT>
RealT generate_random_number(const RealT max,
ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
{
constexpr RealT zero = 0.0;
return generate_random_number(zero, max);
}

// Function object:
template <typename NumberT>
class random_number_generator final
{
std::mutex l;
std::random_device rd;
std::default_random_engine e;

public:
using number_type = NumberT;

public:
random_number_generator() {
detail::randomize_rng(l, e);
}

explicit random_number_generator(const int seed) {
detail::randomize_rng(seed, l, e);
}

random_number_generator(random_number_generator&& rhs)
: e(std::move(rhs.e))
{}

public:
random_number_generator(const random_number_generator&) = delete;
random_number_generator& operator=(const random_number_generator&) = delete;

public:
NumberT operator()() {
return detail::generate_random_number(l, e);
}

NumberT operator()(const NumberT max) {
return detail::generate_random_number(max, l, e);
}

NumberT operator()(const NumberT min, const NumberT max) {
return detail::generate_random_number(min, max, l, e);
}

public:
void seed(const int n) {
detail::randomize_rng(n, l, e);
}
};

} // inline namespace version_1_0

}} // namespace ceph::util

#endif
7 changes: 7 additions & 0 deletions src/test/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ add_executable(unittest_util
add_ceph_unittest(unittest_util ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_util)
target_link_libraries(unittest_util global ${BLKID_LIBRARIES})

# unittest_random
add_executable(unittest_random
test_random.cc
)
add_ceph_unittest(unittest_random ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_random)
target_link_libraries(unittest_random global ${BLKID_LIBRARIES})

# unittest_throttle
add_executable(unittest_throttle
Throttle.cc
Expand Down
Loading

0 comments on commit 8ac5472

Please sign in to comment.