Commit ff85ab6c authored by Christoph Conrads's avatar Christoph Conrads

Compare PRNG batch vs sequential processing

parent 45339bec
......@@ -8,6 +8,7 @@ CXX := g++
CXXFLAGS := -O3 -march=native -DNDEBUG
SOURCES := \
batch-vs-sequential.cpp \
benchmark.cpp rlxt-vs-std.cpp print-random.cpp swc24-vs-native-ints.cpp
EXECUTABLES := $(patsubst %.cpp,%.bin,$(SOURCES))
......
// Copyright 2019 Christoph Conrads
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include "benchmark.hpp"
#include <cassert>
#include <cstdio>
#include <cstdint>
#include <ctime>
#include <limits>
#include <random>
#include <string>
#include <type_traits>
namespace rlxt = ranlux_tools;
template<typename T, std::size_t w, std::size_t s, std::size_t r>
struct sequential_add_with_carry_engine
{
static_assert(std::is_integral<T>::value, "");
static_assert(not std::is_signed<T>::value, "");
static_assert(w <= std::numeric_limits<T>::digits, "");
static_assert(s > 0u, "");
static_assert(r > s, "");
// prototype implementation
static_assert(w == std::numeric_limits<T>::digits, "");
static_assert(w == 8u || w == 16u or w == 32u or w == 64u, "");
using result_type = T;
static constexpr auto long_lag = r;
static constexpr auto short_lag = s;
static constexpr auto word_size = w;
// `std::subtract_with_carry_engine::default_seed` is 19780503. I have no
// idea where this value is coming from so I use its larger prime factor
// instead.
static constexpr auto default_seed = std::uint32_t{387853};
static constexpr T max() { return std::numeric_limits<T>::max(); }
static constexpr T min() { return std::numeric_limits<T>::min(); }
explicit sequential_add_with_carry_engine(std::uint64_t seed=default_seed)
{
auto gen = ranlux_tools::xoshiro128plus(seed);
for(auto& x : xs_)
x = gen();
// ensure entering a periodic sequence
discard(r);
}
T operator() ()
{
using BigInt = ranlux_tools::impl_random_number_engine::big_integer_t<w>;
auto i = index_;
auto j = index_ >= s ? index_ - s : index_ + r - s;
assert(carry_ == 0 or carry_ == 1);
auto x = BigInt{xs_[i]} + xs_[j] + carry_;
xs_[i] = x;
carry_ = x >> w;
index_ = index_ + 1u == r ? 0u : index_ + 1u;
return x;
}
void discard(unsigned long long n)
{
for(auto i = 0ull; i < n; ++i)
(*this)();
}
std::size_t index_ = 0;
T carry_ = 0;
T xs_[r] = { 0 };
};
template<typename T, std::size_t w, std::size_t s, std::size_t r>
struct batch_add_with_carry_engine
{
static_assert(std::is_integral<T>::value, "");
static_assert(not std::is_signed<T>::value, "");
static_assert(w <= std::numeric_limits<T>::digits, "");
static_assert(s > 0u, "");
static_assert(r > s, "");
// prototype implementation
static_assert(w == std::numeric_limits<T>::digits, "");
static_assert(w == 8u || w == 16u or w == 32u or w == 64u, "");
using result_type = T;
static constexpr auto long_lag = r;
static constexpr auto short_lag = s;
static constexpr auto word_size = w;
// `std::subtract_with_carry_engine::default_seed` is 19780503. I have no
// idea where this value is coming from so I use its larger prime factor
// instead.
static constexpr auto default_seed = std::uint32_t{387853};
static constexpr T max() { return std::numeric_limits<T>::max(); }
static constexpr T min() { return std::numeric_limits<T>::min(); }
explicit batch_add_with_carry_engine(std::uint64_t seed=default_seed)
{
auto gen = ranlux_tools::xoshiro128plus(seed);
for(auto& x : xs_)
x = gen();
// ensure entering a periodic sequence
discard(r);
}
T operator() ()
{
if(index_ > 0)
{
auto ret = xs_[index_];
index_ = index_ + 1u == r ? 0u : index_ + 1u;
return ret;
}
using BigInt = ranlux_tools::impl_random_number_engine::big_integer_t<w>;
assert(carry_ == 0 or carry_ == 1);
for(auto i = std::size_t{0}; i < long_lag; ++i)
{
auto j = i >= s ? i - s : i + r - s;
auto x = BigInt{xs_[i]} + xs_[j] + carry_;
xs_[i] = x;
carry_ = x >> w;
assert(carry_ == 0 or carry_ == 1);
}
index_ = 1;
return xs_[0];
}
void discard(unsigned long long n)
{
for(auto i = 0ull; i < n; ++i)
(*this)();
}
std::size_t index_ = 0;
T carry_ = 0;
T xs_[r] = { 0 };
};
template<typename T, std::size_t W, std::size_t S, std::size_t R>
std::string get_name(const sequential_add_with_carry_engine<T, W, S, R>&)
{
constexpr auto FORMAT = "SEQTL-AWC(2^%-2zu, %2zu, %1zu)";
char buffer[80] = { 0 };
snprintf(buffer, sizeof(buffer), FORMAT, W, R, S);
return buffer;
}
template<typename T, std::size_t W, std::size_t S, std::size_t R>
std::string get_name(const batch_add_with_carry_engine<T, W, S, R>&)
{
constexpr auto FORMAT = "BATCH-AWC(2^%-2zu, %2zu, %1zu)";
char buffer[80] = { 0 };
snprintf(buffer, sizeof(buffer), FORMAT, W, R, S);
return buffer;
}
using awc8_seq =
sequential_add_with_carry_engine<std::uint8_t, 8, 5, 8>;
using awc8_batch =
batch_add_with_carry_engine<std::uint8_t, 8, 5, 8>;
using awc16_seq =
sequential_add_with_carry_engine<std::uint16_t, 16, 2, 9>;
using awc16_batch =
batch_add_with_carry_engine<std::uint16_t, 16, 2, 9>;
using awc32_seq =
sequential_add_with_carry_engine<std::uint32_t, 32, 3, 16>;
using awc32_batch =
batch_add_with_carry_engine<std::uint32_t, 32, 3, 16>;
using awc64_seq =
sequential_add_with_carry_engine<std::uint64_t, 64, 10, 72>;
using awc64_batch =
batch_add_with_carry_engine<std::uint64_t, 64, 10, 72>;
int main()
{
constexpr auto num_draws = std::uintmax_t{1000} * 1000u * 1000u;
std::printf(
"%-25s | %10s | %20s | %s\n",
"generator", "time(sec)", "throughput(byte/sec)", "dummy"
);
rlxt::run<rlxt::dummy_engine>(num_draws);
rlxt::run<awc8_seq>(num_draws);
rlxt::run<awc8_batch>(num_draws);
rlxt::run<awc16_seq>(num_draws);
rlxt::run<awc16_batch>(num_draws);
rlxt::run<awc32_seq>(num_draws);
rlxt::run<awc32_batch>(num_draws);
#if RANLUX_TOOLS_HAS_INT128
rlxt::run<awc64_seq>(num_draws);
rlxt::run<awc64_batch>(num_draws);
#endif
}
......@@ -36,19 +36,21 @@ namespace impl_random_number_engine
#endif
template<std::size_t w>
using big_integer_t = typename std::conditional<
w == 8u, std::uint16_t, typename std::conditional<
w == 16u, std::uint32_t, typename std::conditional<
w == 32u, std::uint64_t, typename std::conditional<
w == 64u, unsigned __int128, void
>::type>::type>::type;
>::type>::type>::type>::type;
#if __GNUC__
#pragma GCC diagnostic pop
#endif
#else
template<std::size_t w>
using big_integer_t = typename std::conditional<
w == 8u, std::uint16_t, typename std::conditional<
w == 16u, std::uint32_t, typename std::conditional<
w == 32u, std::uint64_t, void
>::type>::type;
>::type>::type>::type;
#endif
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment