Commit 551d5b99 authored by Christoph Conrads's avatar Christoph Conrads

Benchmark different AWC implementations

Compare explicit overflow checks with integer promotion.
parent 232e4b7e
......@@ -8,8 +8,12 @@ CXX := g++
CXXFLAGS := -O3 -march=native -DNDEBUG
SOURCES := \
awc-vs-swb.cpp batch-vs-sequential.cpp \
benchmark.cpp rlxt-vs-std.cpp print-random.cpp swc24-vs-native-ints.cpp
awc-vs-swb.cpp \
batch-vs-sequential.cpp \
benchmark.cpp \
conditional-vs-promotion.cpp \
rlxt-vs-std.cpp print-random.cpp \
swc24-vs-native-ints.cpp
EXECUTABLES := $(patsubst %.cpp,%.bin,$(SOURCES))
.PHONY: run
......
// 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 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 or 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 add_with_carry_engine(std::uint64_t seed=default_seed)
{
auto gen = rlxt::xoshiro128plus(seed);
for(auto& x : xs_)
x = gen();
// ensure entering a periodic sequence
discard(r);
}
T operator() ()
{
using BigInt = rlxt::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 conditional_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 conditional_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() ()
{
auto i = index_;
auto j = index_ >= s ? index_ - s : index_ + r - s;
assert(carry_ == 0 or carry_ == 1);
auto x = xs_[i];
auto y = T(x + xs_[j]);
auto z = T(y + carry_);
xs_[i] = z;
carry_ = (y < x) or (z < y) ? 1u : 0u;
index_ = index_ + 1u == r ? 0u : index_ + 1u;
return z;
}
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 add_with_carry_engine<T, W, S, R>&)
{
constexpr auto FORMAT = "AWC-big(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 conditional_add_with_carry_engine<T, W, S, R>&)
{
constexpr auto FORMAT = "AWC-cond(2^%-2zu, %2zu, %1zu)";
char buffer[80] = { 0 };
snprintf(buffer, sizeof(buffer), FORMAT, W, R, S);
return buffer;
}
using awc8 =
add_with_carry_engine<std::uint8_t, 8, 5, 8>;
using awc8_cond =
conditional_add_with_carry_engine<std::uint8_t, 8, 5, 8>;
using awc16 =
add_with_carry_engine<std::uint16_t, 16, 2, 9>;
using awc16_cond =
conditional_add_with_carry_engine<std::uint16_t, 16, 2, 9>;
using awc32 =
add_with_carry_engine<std::uint32_t, 32, 3, 16>;
using awc32_cond =
conditional_add_with_carry_engine<std::uint32_t, 32, 3, 16>;
using awc64 =
add_with_carry_engine<std::uint64_t, 64, 10, 72>;
using awc64_cond =
conditional_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>(num_draws);
rlxt::run<awc8_cond>(num_draws);
rlxt::run<awc16>(num_draws);
rlxt::run<awc16_cond>(num_draws);
rlxt::run<awc32>(num_draws);
rlxt::run<awc32_cond>(num_draws);
#if RANLUX_TOOLS_HAS_INT128
rlxt::run<awc64>(num_draws);
#endif
rlxt::run<awc64_cond>(num_draws);
}
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