use criterion::{criterion_group, criterion_main};
use uint::{construct_uint, uint_full_mul_reg};
construct_uint! {
pub struct U256(4);
}
construct_uint! {
pub struct U512(8);
}
impl U256 {
#[inline(always)]
pub fn full_mul(self, other: U256) -> U512 {
U512(uint_full_mul_reg!(U256, 4, self, other))
}
}
use criterion::{black_box, Bencher, BenchmarkId, Criterion};
use num_bigint::BigUint;
use rug::{integer::Order, Integer};
use std::str::FromStr;
criterion_group!(
bigint,
u256_add,
u256_sub,
u256_mul,
u256_mul_full,
u256_div,
u512_div_mod,
u256_rem,
u256_integer_sqrt,
u256_bit_and,
u256_bit_or,
u256_bit_xor,
u256_not,
u256_ord,
u256_shl,
u256_shr,
u256_from_le,
u256_from_be,
u512_add,
u512_sub,
u512_mul,
u512_div,
u512_rem,
u512_integer_sqrt,
u512_mul_u32_vs_u64,
mulmod_u512_vs_biguint_vs_gmp,
conversions,
u512_bit_and,
u512_bit_or,
u512_bit_xor,
u512_not,
u512_ord,
u512_shl,
u512_shr,
u128_mul,
u128_div,
from_fixed_array,
from_str,
);
criterion_main!(bigint);
fn to_biguint(x: U256) -> BigUint {
let bytes = x.to_little_endian();
BigUint::from_bytes_le(&bytes)
}
fn from_biguint(x: BigUint) -> U512 {
let bytes = x.to_bytes_le();
U512::from_little_endian(&bytes)
}
fn to_gmp(x: U256) -> Integer {
let U256(ref arr) = x;
Integer::from_digits(&arr[..], Order::Lsf)
}
fn from_gmp(x: Integer) -> U512 {
let digits = x.to_digits(Order::LsfLe);
U512::from_little_endian(&digits)
}
fn u128_div(c: &mut Criterion) {
let mut group = c.benchmark_group("u128_div");
for input in [(0u64, u64::max_value(), 100u64), (u64::max_value(), u64::max_value(), 99), (42, 42, 100500)] {
group.bench_with_input(BenchmarkId::from_parameter(input.2), &input, |b, (x, y, z)| {
b.iter(|| {
let x = black_box(u128::from(*x) << 64 + u128::from(*y));
black_box(x / u128::from(*z))
})
});
}
group.finish();
}
fn u256_add(c: &mut Criterion) {
let mut group = c.benchmark_group("u256_add");
for input in [(0u64, 1u64), (u64::max_value(), 1), (42, 100500)] {
group.bench_with_input(BenchmarkId::from_parameter(input.0), &input, |b, (x, y)| {
b.iter(|| {
let x = U256::from(*x);
let y = U256::from(*y);
black_box(x.overflowing_add(y).0)
})
});
}
group.finish();
}
fn u256_sub(c: &mut Criterion) {
let mut group = c.benchmark_group("hex_to_bytes");
for input in [(U256::MAX, 1u64), (U256::from(3), 2)] {
group.bench_with_input(BenchmarkId::from_parameter(input.0), &input, |b, (x, y)| {
b.iter(|| {
let y = U256::from(*y);
black_box(x.overflowing_sub(y).0)
})
});
}
group.finish();
}
fn u256_mul(c: &mut Criterion) {
let mut group = c.benchmark_group("u256_mul");
for input in [
(U256::MAX, 1u64),
(U256::from(3), u64::max_value()),
(U256::from_dec_str("21674844646682989462120101885968193938394323990565507610662749").unwrap(), 173),
] {
group.bench_with_input(BenchmarkId::from_parameter(input.1), &input, |b, (x, y)| {
b.iter(|| {
let y = U256::from(*y);
black_box(x.overflowing_mul(y).0)
})
});
}
group.finish();
}
fn u512_div_mod(c: &mut Criterion) {
let mut group = c.benchmark_group("u512_div_mod");
for input in [
(U512::MAX, U512::from(1u64)),
(U512::from(u64::max_value()), U512::from(u32::max_value())),
(U512::from(u64::max_value()), U512::from(u64::max_value() - 1)),
(
U512::from_dec_str("3759751734479964094783137206182536765532905409829204647089173492").unwrap(),
U512::from_dec_str("21674844646682989462120101885968193938394323990565507610662749").unwrap(),
),
(
U512::from_str(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
)
.unwrap(),
U512::from_str(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0",
)
.unwrap(),
),
(
U512::from_dec_str(
"204586912993508866875824356051724947013540127877691549342705710506008362274387533983037847993622361501550043477868832682875761627559574690771211649025"
).unwrap(),
U512::from_dec_str(
"452312848583266388373324160190187140051835877600158453279131187530910662640"
).unwrap(),
),
] {
group.bench_with_input(BenchmarkId::from_parameter(input.1), &input, |b, (x, y)| {
b.iter(|| {
let (q, r) = x.div_mod(*y);
black_box((q, r))
})
});
}
group.finish();
}
fn u256_mul_full(c: &mut Criterion) {
let mut group = c.benchmark_group("hex_to_bytes");
for input in [(U256::from(42), 1u64), (U256::from(3), u64::max_value())] {
group.bench_with_input(BenchmarkId::from_parameter(input.1), &input, |b, (x, y)| {
b.iter(|| {
let y = *y;
let U512(ref u512words) = x.full_mul(U256([y, y, y, y]));
black_box(U256([u512words[0], u512words[2], u512words[2], u512words[3]]))
})
});
}
group.finish();
}
fn u256_div(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]);
c.bench_function("u256_div", move |b| b.iter(|| black_box(one / two)));
}
fn u256_rem(c: &mut Criterion) {
let mut group = c.benchmark_group("u256_rem");
for input in [
(U256::MAX, U256::from(1u64)),
(U256::from(u64::max_value()), U256::from(u64::from(u32::max_value()) + 1)),
(
U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]),
U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]),
),
(
U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(),
U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0").unwrap(),
),
] {
group.bench_with_input(BenchmarkId::from_parameter(input.0), &input, |b, (x, y)| b.iter(|| black_box(x % y)));
}
group.finish();
}
fn u256_integer_sqrt(c: &mut Criterion) {
let mut group = c.benchmark_group("u256_integer_sqrt");
for input in [
U256::from(u64::MAX),
U256::from(u128::MAX) + 1,
U256::from(u128::MAX - 1) * U256::from(u128::MAX - 1) - 1,
U256::MAX,
] {
group.bench_with_input(BenchmarkId::from_parameter(input), &input, |b, x| {
b.iter(|| black_box(x.integer_sqrt().0))
});
}
group.finish();
}
fn u512_pairs() -> Vec<(U512, U512)> {
vec![
(U512::from(1u64), U512::from(0u64)),
(U512::from(u64::max_value()), U512::from(u64::from(u32::max_value()) + 1)),
(
U512([12767554894655550452, 16333049135534778834, 140317443000293558, 598963, 0, 0, 0, 0]),
U512([0, 0, 0, 0, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]),
),
(
U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(),
U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0").unwrap(),
),
]
}
fn u512_add(c: &mut Criterion) {
let mut group = c.benchmark_group("u512_add");
for input in u512_pairs() {
group.bench_with_input(BenchmarkId::from_parameter(input.1), &input, |b, (x, y)| b.iter(|| black_box(x + y)));
}
group.finish();
}
fn u512_sub(c: &mut Criterion) {
let mut group = c.benchmark_group("u512_sub");
for input in u512_pairs() {
group.bench_with_input(BenchmarkId::from_parameter(input.1), &input, |b, (x, y)| {
b.iter(|| black_box(x.overflowing_sub(*y).0))
});
}
group.finish();
}
fn u512_mul(c: &mut Criterion) {
let mut group = c.benchmark_group("u512_mul");
for input in u512_pairs() {
group.bench_with_input(BenchmarkId::from_parameter(input.1), &input, |b, (x, y)| {
b.iter(|| black_box(x.overflowing_mul(*y).0))
});
}
group.finish();
}
fn u512_integer_sqrt(c: &mut Criterion) {
let mut group = c.benchmark_group("u512_integer_sqrt");
for input in [
U512::from(u32::MAX) + 1,
U512::from(u64::MAX),
(U512::from(u128::MAX) + 1) * (U512::from(u128::MAX) + 1),
U256::MAX.full_mul(U256::MAX) - 1,
U512::MAX,
] {
group.bench_with_input(BenchmarkId::from_parameter(input), &input, |b, x| {
b.iter(|| black_box(x.integer_sqrt().0))
});
}
group.finish();
}
fn u512_div(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
let two = U512([
11707750893627518758,
17679501210898117940,
2472932874039724966,
11177683849610900539,
2096410819092764509,
8483673822214032535,
36306297304129857,
3453,
]);
c.bench_function("u512_div", move |b| b.iter(|| black_box(one / two)));
}
fn u512_rem(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
let two = U512([
11707750893627518758,
17679501210898117940,
2472932874039724966,
11177683849610900539,
2096410819092764509,
8483673822214032535,
36306297304129857,
3453,
]);
c.bench_function("u512_rem", move |b| b.iter(|| black_box(one % two)));
}
fn conversions(c: &mut Criterion) {
let mut group = c.benchmark_group("conversions biguint vs gmp");
for input in [0, 42, u64::MAX] {
group.bench_with_input(BenchmarkId::new("BigUint", input), &input, |b, i| bench_convert_to_biguit(b, *i));
group.bench_with_input(BenchmarkId::new("GMP", input), &input, |b, i| bench_convert_to_gmp(b, *i));
}
group.finish();
}
fn bench_convert_to_biguit(b: &mut Bencher, i: u64) {
let z = U256::from(i);
let z512 = U512::from(i);
b.iter(|| {
let zb = to_biguint(z);
assert_eq!(from_biguint(zb), z512);
});
}
fn bench_convert_to_gmp(b: &mut Bencher, i: u64) {
let z = U256::from(i);
let z512 = U512::from(i);
b.iter(|| {
let zb = to_gmp(z);
assert_eq!(from_gmp(zb), z512);
});
}
fn u512_mul_u32_vs_u64(c: &mut Criterion) {
let ms = vec![1u32, 42, 10_000_001, u32::MAX];
let mut group = c.benchmark_group("multiply u512 by u32 vs u64");
for input in ms {
group.bench_with_input(BenchmarkId::new("u32", input), &input, |b, i| bench_u512_mul_u32(b, *i));
group.bench_with_input(BenchmarkId::new("u64", input), &input, |b, i| bench_u512_mul_u64(b, u64::from(*i)));
}
group.finish();
}
fn bench_u512_mul_u32(b: &mut Bencher, i: u32) {
let x = U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
b.iter(|| black_box(x * i));
}
fn bench_u512_mul_u64(b: &mut Bencher, i: u64) {
let x = U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
b.iter(|| black_box(x * i));
}
fn mulmod_u512_vs_biguint_vs_gmp(c: &mut Criterion) {
let mods = vec![
U256::from(1u64),
U256::from(10_000_001u64),
U256::from(u64::max_value()),
U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1").unwrap(),
];
let mut group = c.benchmark_group("mulmod u512 vs biguint vs gmp");
for input in mods {
group.bench_with_input(BenchmarkId::new("u512", input), &input, |b, i| bench_u512_mulmod(b, *i));
group.bench_with_input(BenchmarkId::new("BigUint", input), &input, |b, i| bench_biguint_mulmod(b, *i));
group.bench_with_input(BenchmarkId::new("GMP", input), &input, |b, i| bench_gmp_mulmod(b, *i));
}
group.finish();
}
fn bench_biguint_mulmod(b: &mut Bencher, z: U256) {
let x = U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
let y = U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
b.iter(|| {
let w = to_biguint(x) * to_biguint(y);
black_box(from_biguint(w % to_biguint(z)))
});
}
fn bench_gmp_mulmod(b: &mut Bencher, z: U256) {
let x = U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
let y = U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
b.iter(|| {
let w = to_gmp(x) * to_gmp(y);
black_box(from_gmp(w % to_gmp(z)))
});
}
fn bench_u512_mulmod(b: &mut Bencher, z: U256) {
let x = U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
let y = U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap();
let z = U512([z.0[0], z.0[1], z.0[2], z.0[3], 0, 0, 0, 0]);
b.iter(|| {
let w = x.overflowing_mul(y).0;
black_box(w % z)
});
}
fn u128_mul(c: &mut Criterion) {
c.bench_function("u128_mul", |b| b.iter(|| black_box(12345u128 * u128::from(u64::max_value()))));
}
fn u256_bit_and(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]);
c.bench_function("u256_bit_and", move |b| b.iter(|| black_box(one & two)));
}
fn u512_bit_and(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
let two = U512([
11707750893627518758,
17679501210898117940,
2472932874039724966,
11177683849610900539,
2096410819092764509,
8483673822214032535,
36306297304129857,
3453,
]);
c.bench_function("u512_bit_and", move |b| b.iter(|| black_box(one & two)));
}
fn u256_bit_xor(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]);
c.bench_function("u256_bit_xor", move |b| b.iter(|| black_box(one ^ two)));
}
fn u512_bit_xor(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
let two = U512([
11707750893627518758,
17679501210898117940,
2472932874039724966,
11177683849610900539,
2096410819092764509,
8483673822214032535,
36306297304129857,
3453,
]);
c.bench_function("u512_bit_xor", move |b| b.iter(|| black_box(one ^ two)));
}
fn u256_bit_or(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]);
c.bench_function("u256_bit_or", move |b| b.iter(|| black_box(one | two)));
}
fn u512_bit_or(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
let two = U512([
11707750893627518758,
17679501210898117940,
2472932874039724966,
11177683849610900539,
2096410819092764509,
8483673822214032535,
36306297304129857,
3453,
]);
c.bench_function("u512_bit_or", move |b| b.iter(|| black_box(one | two)));
}
fn u256_not(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
c.bench_function("u256_not", move |b| b.iter(|| black_box(!one)));
}
fn u512_not(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
c.bench_function("u512_not", move |b| b.iter(|| black_box(!one)));
}
fn u256_shl(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
c.bench_function("u256_shl", move |b| b.iter(|| black_box(one << 128)));
}
fn u512_shl(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
c.bench_function("u512_shl", move |b| b.iter(|| black_box(one >> 128)));
}
fn u256_shr(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
c.bench_function("u256_shr", move |b| b.iter(|| black_box(one >> 128)));
}
fn u512_shr(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
c.bench_function("u512_shr", move |b| b.iter(|| black_box(one >> 128)));
}
fn u256_ord(c: &mut Criterion) {
let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]);
let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]);
c.bench_function("u256_ord", move |b| b.iter(|| black_box(one) < black_box(two)));
}
fn u512_ord(c: &mut Criterion) {
let one = U512([
8326634216714383706,
15837136097609390493,
13004317189126203332,
7031796866963419685,
12767554894655550452,
16333049135534778834,
140317443000293558,
598963,
]);
let two = U512([
11707750893627518758,
17679501210898117940,
2472932874039724966,
11177683849610900539,
2096410819092764509,
8483673822214032535,
36306297304129857,
3453,
]);
c.bench_function("u512_ord", move |b| b.iter(|| black_box(one) < black_box(two)));
}
fn u256_from_le(c: &mut Criterion) {
c.bench_function("u256_from_le", |b| {
b.iter(|| {
let raw = black_box([
1u8, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
101, 103, 107, 109, 113, 127,
]);
black_box(U256::from_little_endian(&raw[..]))
})
});
}
fn u256_from_be(c: &mut Criterion) {
c.bench_function("u256_from_be", |b| {
b.iter(|| {
let raw = black_box([
1u8, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
101, 103, 107, 109, 113, 127,
]);
black_box(U256::from_big_endian(&raw[..]))
})
});
}
fn from_fixed_array(c: &mut Criterion) {
let ary512: [u8; 64] = [
255, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 213, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123,
];
let ary256: [u8; 32] =
[255, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 213, 0, 0, 0, 0, 0, 0];
c.bench_function("from_fixed_array", move |b| {
b.iter(|| {
let _: U512 = black_box(U512::from_big_endian(black_box(&ary512)));
let _: U256 = black_box(U256::from_big_endian(black_box(&ary256)));
})
});
}
fn from_str(c: &mut Criterion) {
c.bench_function("from_str", move |b| {
b.iter(|| {
black_box(U512::from_str(black_box("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")).unwrap());
black_box(U512::from_str(black_box("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")).unwrap());
black_box(U512::from_str(black_box("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")).unwrap());
})
});
}