mirror of
https://github.com/luxfi/intx.git
synced 2026-10-30 16:22:26 +00:00
613 lines
17 KiB
C++
613 lines
17 KiB
C++
// intx: extended precision integer library.
|
|
// Copyright 2019 Pawel Bylica.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
|
|
#include "../experimental/addmod.hpp"
|
|
#include <benchmark/benchmark.h>
|
|
#include <intx/intx.hpp>
|
|
#include <test/utils/gmp.hpp>
|
|
#include <test/utils/random.hpp>
|
|
|
|
#if __clang_major__ >= 11 && __clang_major__ <= 13 && !defined(__apple_build_version__)
|
|
#define INTX_HAS_EXTINT 1
|
|
#else
|
|
#define INTX_HAS_EXTINT 0
|
|
#endif
|
|
|
|
using namespace intx;
|
|
using namespace intx::test;
|
|
|
|
namespace
|
|
{
|
|
[[gnu::noinline]] auto add(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
return x + y;
|
|
}
|
|
|
|
[[gnu::noinline]] auto add(const uint512& x, const uint512& y) noexcept
|
|
{
|
|
return x + y;
|
|
}
|
|
|
|
[[gnu::noinline]] auto sub(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
return x - y;
|
|
}
|
|
|
|
[[gnu::noinline]] auto sub(const uint512& x, const uint512& y) noexcept
|
|
{
|
|
return x - y;
|
|
}
|
|
|
|
[[gnu::noinline]] auto exp(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
return intx::exp(x, y);
|
|
}
|
|
|
|
template <typename ArgT, div_result<ArgT> DivFn(const ArgT&, const ArgT&)>
|
|
void div(benchmark::State& state) noexcept
|
|
{
|
|
const auto division_set_id = [&state]() noexcept {
|
|
switch (state.range(0))
|
|
{
|
|
case 64:
|
|
return x_64;
|
|
case 128:
|
|
return x_128;
|
|
case 192:
|
|
return x_192;
|
|
case 256:
|
|
return lt_256;
|
|
default:
|
|
state.SkipWithError("unexpected argument");
|
|
return x_64;
|
|
}
|
|
}();
|
|
|
|
const auto& xs = test::get_samples<ArgT>(sizeof(ArgT) == sizeof(uint256) ? x_256 : x_512);
|
|
const auto& ys = test::get_samples<ArgT>(division_set_id);
|
|
|
|
while (state.KeepRunningBatch(static_cast<benchmark::IterationCount>(xs.size())))
|
|
{
|
|
for (size_t i = 0; i < xs.size(); ++i)
|
|
{
|
|
auto _ = DivFn(xs[i], ys[i]);
|
|
benchmark::DoNotOptimize(_);
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(div<uint256, udivrem>)->DenseRange(64, 256, 64);
|
|
BENCHMARK(div<uint256, gmp::udivrem>)->DenseRange(64, 256, 64);
|
|
BENCHMARK(div<uint512, udivrem>)->DenseRange(64, 256, 64);
|
|
BENCHMARK(div<uint512, gmp::udivrem>)->DenseRange(64, 256, 64);
|
|
|
|
|
|
template <uint256 ModFn(const uint256&, const uint256&, const uint256&)>
|
|
void mod(benchmark::State& state)
|
|
{
|
|
const auto mod_set_id = [&state]() noexcept {
|
|
switch (state.range(0))
|
|
{
|
|
case 64:
|
|
return x_64;
|
|
case 128:
|
|
return x_128;
|
|
case 192:
|
|
return x_192;
|
|
case 256:
|
|
return lt_256;
|
|
default:
|
|
state.SkipWithError("unexpected argument");
|
|
return x_64;
|
|
}
|
|
}();
|
|
|
|
const auto& xs = test::get_samples<uint256>(x_256);
|
|
const auto& ys = test::get_samples<uint256>(y_256);
|
|
const auto& ms = test::get_samples<uint256>(mod_set_id);
|
|
|
|
while (state.KeepRunningBatch(static_cast<benchmark::IterationCount>(xs.size())))
|
|
{
|
|
for (size_t i = 0; i < xs.size(); ++i)
|
|
{
|
|
auto _ = ModFn(xs[i], ys[i], ms[i]);
|
|
benchmark::DoNotOptimize(_);
|
|
}
|
|
}
|
|
}
|
|
#define ARGS DenseRange(64, 256, 64)
|
|
BENCHMARK(mod<addmod>)->ARGS;
|
|
BENCHMARK(mod<addmod_public>)->ARGS;
|
|
BENCHMARK(mod<addmod_simple>)->ARGS;
|
|
BENCHMARK(mod<addmod_prenormalize>)->ARGS;
|
|
BENCHMARK(mod<addmod_daosvik_v1>)->ARGS;
|
|
BENCHMARK(mod<addmod_daosvik_v2>)->ARGS;
|
|
BENCHMARK(mod<mulmod>)->ARGS;
|
|
#undef ARGS
|
|
|
|
template <uint256 ModFn(const uint256&, const uint256&, const uint256&)>
|
|
void ecmod(benchmark::State& state)
|
|
{
|
|
// Samples such x <= m, y <= m.
|
|
const auto& xs = test::get_samples<uint256>(lt_x_256);
|
|
const auto& ys = test::get_samples<uint256>(lt_256);
|
|
const auto& ms = test::get_samples<uint256>(x_256);
|
|
|
|
while (state.KeepRunningBatch(static_cast<benchmark::IterationCount>(xs.size())))
|
|
{
|
|
for (size_t i = 0; i < xs.size(); ++i)
|
|
{
|
|
auto _ = ModFn(xs[i], ys[i], ms[i]);
|
|
benchmark::DoNotOptimize(_);
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(ecmod<addmod_public>);
|
|
BENCHMARK(ecmod<addmod_simple>);
|
|
BENCHMARK(ecmod<addmod_prenormalize>);
|
|
BENCHMARK(ecmod<addmod_daosvik_v1>);
|
|
BENCHMARK(ecmod<addmod_daosvik_v2>);
|
|
BENCHMARK(ecmod<mulmod>);
|
|
|
|
|
|
template <unsigned N>
|
|
[[gnu::noinline]] auto public_mul(const intx::uint<N>& x, const intx::uint<N>& y) noexcept
|
|
{
|
|
return x * y;
|
|
}
|
|
|
|
template <unsigned N>
|
|
[[gnu::noinline]] auto umul_(const intx::uint<N>& x, const intx::uint<N>& y) noexcept
|
|
{
|
|
return intx::umul(x, y);
|
|
}
|
|
|
|
inline auto inline_add(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
return x + y;
|
|
}
|
|
|
|
inline auto inline_sub(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
return x - y;
|
|
}
|
|
|
|
|
|
inline auto inline_add(const uint512& x, const uint512& y) noexcept
|
|
{
|
|
return x + y;
|
|
}
|
|
|
|
|
|
inline auto inline_sub(const uint512& x, const uint512& y) noexcept
|
|
{
|
|
return x - y;
|
|
}
|
|
|
|
|
|
template <typename ResultT, typename ArgT, ResultT BinOp(const ArgT&, const ArgT&)>
|
|
void binop(benchmark::State& state)
|
|
{
|
|
const auto& xs = test::get_samples<ArgT>(sizeof(ArgT) == sizeof(uint256) ? x_256 : x_512);
|
|
const auto& ys = test::get_samples<ArgT>(sizeof(ArgT) == sizeof(uint256) ? y_256 : y_512);
|
|
|
|
while (state.KeepRunningBatch(static_cast<benchmark::IterationCount>(xs.size())))
|
|
{
|
|
for (size_t i = 0; i < xs.size(); ++i)
|
|
{
|
|
auto _ = BinOp(xs[i], ys[i]);
|
|
benchmark::DoNotOptimize(_);
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(binop<uint256, uint256, add>);
|
|
BENCHMARK(binop<uint256, uint256, inline_add>);
|
|
BENCHMARK(binop<uint256, uint256, sub>);
|
|
BENCHMARK(binop<uint256, uint256, inline_sub>);
|
|
BENCHMARK(binop<uint256, uint256, public_mul>);
|
|
BENCHMARK(binop<uint256, uint256, gmp::mul>);
|
|
|
|
BENCHMARK(binop<uint512, uint256, umul_>);
|
|
BENCHMARK(binop<uint512, uint256, gmp::mul_full>);
|
|
|
|
BENCHMARK(binop<uint512, uint512, add>);
|
|
BENCHMARK(binop<uint512, uint512, inline_add>);
|
|
BENCHMARK(binop<uint512, uint512, sub>);
|
|
BENCHMARK(binop<uint512, uint512, inline_sub>);
|
|
BENCHMARK(binop<uint512, uint512, public_mul>);
|
|
BENCHMARK(binop<uint512, uint512, gmp::mul>);
|
|
|
|
template <unsigned N>
|
|
[[gnu::noinline]] intx::uint<N> shl_public(const intx::uint<N>& x, const uint64_t& y) noexcept
|
|
{
|
|
return x << y;
|
|
}
|
|
|
|
template <unsigned N>
|
|
[[gnu::noinline]] intx::uint<N> shl_public(const intx::uint<N>& x, const intx::uint<N>& y) noexcept
|
|
{
|
|
return x << y;
|
|
}
|
|
|
|
[[gnu::noinline]] intx::uint256 shl_halves(const intx::uint256& x, const uint64_t& shift) noexcept
|
|
{
|
|
constexpr auto num_bits = 256;
|
|
constexpr auto half_bits = num_bits / 2;
|
|
|
|
const auto xlo = uint128{x[0], x[1]};
|
|
|
|
if (shift < half_bits)
|
|
{
|
|
const auto lo = xlo << shift;
|
|
|
|
const auto xhi = uint128{x[2], x[3]};
|
|
|
|
// Find the part moved from lo to hi.
|
|
// The shift right here can be invalid:
|
|
// for shift == 0 => lshift == half_bits.
|
|
// Split it into 2 valid shifts by (rshift - 1) and 1.
|
|
const auto rshift = half_bits - shift;
|
|
const auto lo_overflow = (xlo >> (rshift - 1)) >> 1;
|
|
const auto hi = (xhi << shift) | lo_overflow;
|
|
return {lo[0], lo[1], hi[0], hi[1]};
|
|
}
|
|
|
|
// This check is only needed if we want "defined" behavior for shifts
|
|
// larger than size of the Int.
|
|
if (shift < num_bits)
|
|
{
|
|
const auto hi = xlo << (shift - half_bits);
|
|
return {0, 0, hi[0], hi[1]};
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
[[gnu::noinline]] intx::uint256 shl_halves(
|
|
const intx::uint256& x, const uint256& big_shift) noexcept
|
|
{
|
|
if (INTX_UNLIKELY((big_shift[3] | big_shift[2] | big_shift[1]) != 0))
|
|
return 0;
|
|
|
|
const auto shift = big_shift[0];
|
|
|
|
|
|
constexpr auto num_bits = 256;
|
|
constexpr auto half_bits = num_bits / 2;
|
|
|
|
const auto xlo = uint128{x[0], x[1]};
|
|
|
|
if (shift < half_bits)
|
|
{
|
|
const auto lo = xlo << shift;
|
|
|
|
const auto xhi = uint128{x[2], x[3]};
|
|
|
|
// Find the part moved from lo to hi.
|
|
// The shift right here can be invalid:
|
|
// for shift == 0 => lshift == half_bits.
|
|
// Split it into 2 valid shifts by (rshift - 1) and 1.
|
|
const auto rshift = half_bits - shift;
|
|
const auto lo_overflow = (xlo >> (rshift - 1)) >> 1;
|
|
const auto hi = (xhi << shift) | lo_overflow;
|
|
return {lo[0], lo[1], hi[0], hi[1]};
|
|
}
|
|
|
|
// This check is only needed if we want "defined" behavior for shifts
|
|
// larger than size of the Int.
|
|
if (shift < num_bits)
|
|
{
|
|
const auto hi = xlo << (shift - half_bits);
|
|
return {0, 0, hi[0], hi[1]};
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if INTX_HAS_EXTINT
|
|
[[gnu::noinline]] static intx::uint256 shl_llvm(const intx::uint256& x, const uint64_t& y) noexcept
|
|
{
|
|
unsigned _ExtInt(256) a; // NOLINT(cppcoreguidelines-init-variables)
|
|
std::memcpy(&a, &x, sizeof(a));
|
|
const auto b = a << y;
|
|
uint256 r;
|
|
std::memcpy(&r, &b, sizeof(r));
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
|
|
template <typename ArgT, typename ShiftT, ArgT ShiftFn(const ArgT&, const ShiftT&)>
|
|
void shift(benchmark::State& state)
|
|
{
|
|
const auto& shift_samples_id = [&state]() noexcept {
|
|
switch (state.range(0))
|
|
{
|
|
case -1:
|
|
return shift_mixed;
|
|
case 0:
|
|
return shift_w0;
|
|
case 1:
|
|
return shift_w1;
|
|
case 2:
|
|
return shift_w2;
|
|
case 3:
|
|
return shift_w3;
|
|
default:
|
|
state.SkipWithError("unexpected argument");
|
|
return shift_mixed;
|
|
}
|
|
}();
|
|
|
|
const auto& xs = test::get_samples<ArgT>(sizeof(ArgT) == sizeof(uint256) ? x_256 : x_512);
|
|
const auto& raw_shifts = test::get_samples<uint64_t>(shift_samples_id);
|
|
std::array<ShiftT, test::num_samples> shifts{};
|
|
std::copy(std::cbegin(raw_shifts), std::cend(raw_shifts), std::begin(shifts));
|
|
|
|
while (state.KeepRunningBatch(static_cast<benchmark::IterationCount>(xs.size())))
|
|
{
|
|
for (size_t i = 0; i < xs.size(); ++i)
|
|
{
|
|
auto _ = ShiftFn(xs[i], shifts[i]);
|
|
benchmark::DoNotOptimize(_);
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(shift<uint256, uint256, shl_public>)->DenseRange(-1, 3);
|
|
BENCHMARK(shift<uint256, uint256, shl_halves>)->DenseRange(-1, 3);
|
|
BENCHMARK(shift<uint256, uint64_t, shl_public>)->DenseRange(-1, 3);
|
|
BENCHMARK(shift<uint256, uint64_t, shl_halves>)->DenseRange(-1, 3);
|
|
#if INTX_HAS_EXTINT
|
|
BENCHMARK(shift<uint256, uint64_t, shl_llvm>)->DenseRange(-1, 3);
|
|
#endif
|
|
BENCHMARK(shift<uint512, uint512, shl_public>)->DenseRange(-1, 3);
|
|
BENCHMARK(shift<uint512, uint64_t, shl_public>)->DenseRange(-1, 3);
|
|
|
|
[[gnu::noinline]] bool lt_public(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
return x < y;
|
|
}
|
|
|
|
[[gnu::noinline]] bool lt_sub(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
return subc(x, y).carry;
|
|
}
|
|
|
|
[[gnu::noinline]] bool lt_split(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
auto xp = uint128{x[2], x[3]};
|
|
auto yp = uint128{y[2], y[3]};
|
|
if (xp == yp)
|
|
{
|
|
xp = uint128{x[0], x[1]};
|
|
yp = uint128{y[0], y[1]};
|
|
}
|
|
return xp < yp;
|
|
}
|
|
|
|
[[gnu::noinline]] bool lt_wordcmp(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
for (size_t i = 3; i >= 1; --i)
|
|
{
|
|
if (x[i] < y[i])
|
|
return true;
|
|
if (x[i] > y[i])
|
|
return false;
|
|
}
|
|
return x[0] < y[0];
|
|
}
|
|
|
|
/// Find the first different word and compares it.
|
|
/// Proposed in https://github.com/chfast/intx/pull/269.
|
|
[[gnu::noinline]] bool lt_ne(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
for (auto i = uint256::num_words - 1; i > 0; --i)
|
|
{
|
|
if (x[i] != y[i])
|
|
return x[i] < y[i];
|
|
}
|
|
return x[0] < y[0];
|
|
}
|
|
|
|
/// A modification of lt_ne(). Smaller code, saves one cmp instruction.
|
|
[[gnu::noinline]] bool lt_ne2(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
auto a = x[3];
|
|
auto b = y[3];
|
|
for (auto i = uint256::num_words - 1; i > 0;)
|
|
{
|
|
if (a != b)
|
|
break;
|
|
--i;
|
|
a = x[i];
|
|
b = y[i];
|
|
}
|
|
return a < b;
|
|
}
|
|
|
|
[[gnu::noinline]] bool lt_halves(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
const auto xhi = uint128{x[2], x[3]};
|
|
const auto xlo = uint128{x[0], x[1]};
|
|
const auto yhi = uint128{y[2], y[3]};
|
|
const auto ylo = uint128{y[0], y[1]};
|
|
return int{xhi < yhi} | (int{xhi == yhi} & int{xlo < ylo});
|
|
}
|
|
|
|
#if INTX_HAS_EXTINT
|
|
[[gnu::noinline]] bool lt_llvm(const uint256& x, const uint256& y) noexcept
|
|
{
|
|
unsigned _ExtInt(256) a; // NOLINT(cppcoreguidelines-init-variables)
|
|
unsigned _ExtInt(256) b; // NOLINT(cppcoreguidelines-init-variables)
|
|
std::memcpy(&a, &x, sizeof(a));
|
|
std::memcpy(&b, &y, sizeof(b));
|
|
return a < b;
|
|
}
|
|
#endif
|
|
|
|
template <bool CmpFn(const uint256&, const uint256&)>
|
|
void compare(benchmark::State& state)
|
|
{
|
|
const auto [x_id, y_id] = [&state]() noexcept -> std::pair<samples_set_id, samples_set_id> {
|
|
switch (state.range(0))
|
|
{
|
|
case 0:
|
|
return {x_256_mixed, y_256_mixed};
|
|
case 64:
|
|
return {x_64, y_64};
|
|
case 128:
|
|
return {x_128, y_128};
|
|
case 192:
|
|
return {x_192, y_192};
|
|
case 256:
|
|
return {x_256, y_256};
|
|
default:
|
|
state.SkipWithError("unexpected argument");
|
|
return {};
|
|
}
|
|
}();
|
|
|
|
const auto& xs = test::get_samples<uint256>(x_id);
|
|
const auto& ys = test::get_samples<uint256>(y_id);
|
|
|
|
while (state.KeepRunningBatch(static_cast<benchmark::IterationCount>(xs.size())))
|
|
{
|
|
for (size_t i = 0; i < xs.size(); ++i)
|
|
{
|
|
auto _ = CmpFn(xs[i], ys[i]);
|
|
benchmark::DoNotOptimize(_);
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(compare<lt_public>)->DenseRange(0, 256, 64);
|
|
BENCHMARK(compare<lt_sub>)->DenseRange(0, 256, 64);
|
|
BENCHMARK(compare<lt_split>)->DenseRange(0, 256, 64);
|
|
BENCHMARK(compare<lt_wordcmp>)->DenseRange(0, 256, 64);
|
|
BENCHMARK(compare<lt_ne>)->DenseRange(0, 256, 64);
|
|
BENCHMARK(compare<lt_ne2>)->DenseRange(0, 256, 64);
|
|
BENCHMARK(compare<lt_halves>)->DenseRange(0, 256, 64);
|
|
#if INTX_HAS_EXTINT
|
|
BENCHMARK(compare<lt_llvm>)->DenseRange(0, 256, 64);
|
|
#endif
|
|
|
|
void exponentiation(benchmark::State& state)
|
|
{
|
|
const auto exponent_set_id = [&state]() noexcept {
|
|
switch (state.range(0))
|
|
{
|
|
case 64:
|
|
return x_64;
|
|
case 128:
|
|
return x_128;
|
|
case 192:
|
|
return x_192;
|
|
case 256:
|
|
return x_256;
|
|
default:
|
|
state.SkipWithError("unexpected argument");
|
|
return x_64;
|
|
}
|
|
}();
|
|
|
|
const auto& bs = test::get_samples<uint256>(x_256);
|
|
const auto& es = test::get_samples<uint256>(exponent_set_id);
|
|
|
|
while (state.KeepRunningBatch(static_cast<benchmark::IterationCount>(bs.size())))
|
|
{
|
|
for (size_t i = 0; i < bs.size(); ++i)
|
|
{
|
|
auto _ = exp(bs[i], es[i]);
|
|
benchmark::DoNotOptimize(_);
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(exponentiation)->DenseRange(64, 256, 64);
|
|
|
|
void exponentiation2(benchmark::State& state)
|
|
{
|
|
const auto base = uint256{2};
|
|
const auto exponent = static_cast<unsigned>(state.range(0));
|
|
|
|
for ([[maybe_unused]] auto _ : state)
|
|
{
|
|
auto e = exp(base, exponent);
|
|
benchmark::DoNotOptimize(e);
|
|
}
|
|
}
|
|
BENCHMARK(exponentiation2)->Arg(0)->RangeMultiplier(2)->Range(64, 512);
|
|
|
|
void count_sigificant_words_256(benchmark::State& state)
|
|
{
|
|
auto s = static_cast<unsigned>(state.range(0));
|
|
auto x = s != 0 ? uint256(0xff) << (s * 32 - 17) : uint256(0);
|
|
benchmark::DoNotOptimize(x);
|
|
benchmark::ClobberMemory();
|
|
|
|
for ([[maybe_unused]] auto _ : state)
|
|
{
|
|
benchmark::ClobberMemory();
|
|
auto w = count_significant_words(x);
|
|
benchmark::DoNotOptimize(w);
|
|
}
|
|
}
|
|
BENCHMARK(count_sigificant_words_256)->DenseRange(0, 8);
|
|
|
|
template <typename Int>
|
|
void to_string(benchmark::State& state)
|
|
{
|
|
// Pick random operands. Keep the divisor small, because this is the worst
|
|
// case for most algorithms.
|
|
lcg<Int> rng(get_seed());
|
|
|
|
constexpr size_t size = 1000;
|
|
std::vector<Int> input(size);
|
|
for (auto& x : input)
|
|
x = rng();
|
|
|
|
while (state.KeepRunningBatch(size))
|
|
{
|
|
for (size_t i = 0; i < size; ++i)
|
|
{
|
|
auto s = intx::to_string(input[i]);
|
|
benchmark::DoNotOptimize(s.data());
|
|
}
|
|
}
|
|
}
|
|
BENCHMARK(to_string<uint128>);
|
|
BENCHMARK(to_string<uint256>);
|
|
BENCHMARK(to_string<uint512>);
|
|
|
|
|
|
template <typename Int>
|
|
[[gnu::noinline]] auto load_be(const uint8_t* data) noexcept
|
|
{
|
|
return intx::be::unsafe::load<Int>(data);
|
|
}
|
|
|
|
template <typename Int>
|
|
[[gnu::noinline]] auto store_be(uint8_t* data, const Int& v) noexcept
|
|
{
|
|
intx::be::unsafe::store(data, v);
|
|
}
|
|
|
|
template <typename Int>
|
|
void load_store_be(benchmark::State& state)
|
|
{
|
|
uint8_t load_buffer[sizeof(Int) + 7]{};
|
|
const auto unaligned_load_ptr = load_buffer + 7;
|
|
uint8_t store_buffer[sizeof(Int) + 1]{};
|
|
const auto unaligned_store_ptr = store_buffer + 1;
|
|
|
|
for ([[maybe_unused]] auto _ : state)
|
|
{
|
|
auto v = load_be<Int>(unaligned_load_ptr);
|
|
store_be(unaligned_store_ptr, v);
|
|
}
|
|
}
|
|
BENCHMARK(load_store_be<uint128>);
|
|
BENCHMARK(load_store_be<uint256>);
|
|
BENCHMARK(load_store_be<uint512>);
|
|
} // namespace
|
|
|
|
BENCHMARK_MAIN();
|