Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
#![feature(test)]
extern crate test;
use bitvec::prelude::*;
use test::Bencher;
#[bench]
fn bitwise_eq(bench: &mut Bencher) {
let a = bitarr![usize, Lsb0; 0; 500];
let b = bitarr![usize, Msb0; 0; 500];
bench.iter(|| {
a.iter()
.by_vals()
.zip(b.iter().by_vals())
.all(|(a, b)| a == b)
});
}
#[bench]
fn plain_eq(bench: &mut Bencher) {
let a = bitarr![usize, Lsb0; 0; 500];
let b = bitarr![usize, Msb0; 0; 500];
bench.iter(|| a == b);
}
#[bench]
fn lsb0_accel_eq(bench: &mut Bencher) {
let a = bitarr![usize, Lsb0; 0; 500];
let b = bitarr![usize, Lsb0; 0; 500];
bench.iter(|| a == b);
}
#[bench]
fn msb0_accel_eq(bench: &mut Bencher) {
let a = bitarr![usize, Msb0; 0; 500];
let b = bitarr![usize, Msb0; 0; 500];
bench.iter(|| a == b);
}
+32
View File
@@ -0,0 +1,32 @@
#![feature(test)]
extern crate test;
use bitvec::prelude::*;
use test::Bencher;
const LEN: usize = 1 << 10;
#[bench]
fn iter_proxy(bench: &mut Bencher) {
let a = bits![1; LEN];
bench.iter(|| a.iter().all(|b| *b));
}
#[bench]
fn iter_ref(bench: &mut Bencher) {
let a = bits![1; LEN];
bench.iter(|| a.iter().by_refs().all(|b| *b));
}
#[bench]
fn iter_val(bench: &mut Bencher) {
let a = bits![1; LEN];
bench.iter(|| a.iter().by_vals().all(|b| b));
}
#[bench]
fn iter_bools(bench: &mut Bencher) {
let a = [true; LEN];
bench.iter(|| a.iter().copied().all(|b| b));
}
+153
View File
@@ -0,0 +1,153 @@
/*! Macro construction benchmarks.
This is taken from [issue #28], which noted that the `bitvec![bit; rep]`
expansion was horribly inefficient.
This benchmark crate should be used for all macro performance recording, and
compare the macros against `vec!`. While `vec!` will always be faster, because
`bitvec!` does more work than `vec!`, they should at least be close.
Original performance was 10,000x slower. Performance after the fix for #28 was
within 20ns.
[issue #28]: https://github.com/myrrlyn/bitvec/issues/28
!*/
#![feature(test)]
extern crate test;
use bitvec::prelude::*;
use test::Bencher;
#[bench]
fn bits_seq_u8(b: &mut Bencher) {
b.iter(|| {
bitarr![u8, LocalBits;
0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 0, 1,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 0, 1, 0, 0, 0, 0, 1,
]
});
}
#[bench]
fn bits_seq_u16(b: &mut Bencher) {
b.iter(|| {
bitarr![u16, LocalBits;
0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 0, 1,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 0, 1, 0, 0, 0, 0, 1,
]
});
}
#[bench]
fn bits_seq_u32(b: &mut Bencher) {
b.iter(|| {
bitarr![u32, LocalBits;
0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 0, 1,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 0, 1, 0, 0, 0, 0, 1,
]
});
}
#[bench]
#[cfg(target_pointer_width = "64")]
fn bits_seq_u64(b: &mut Bencher) {
b.iter(|| {
bitarr![u64, LocalBits;
0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 0, 1,
0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1,
0, 0, 1, 0, 0, 0, 0, 1,
]
});
}
// The repetition macros run at compile time, so should bench at zero.
#[bench]
fn bits_rep_u8(b: &mut Bencher) {
b.iter(|| bitarr![u8, LocalBits; 0; 120]);
b.iter(|| bitarr![u8, LocalBits; 1; 120]);
}
#[bench]
fn bits_rep_u16(b: &mut Bencher) {
b.iter(|| bitarr![u16, LocalBits; 0; 120]);
b.iter(|| bitarr![u16, LocalBits; 1; 120]);
}
#[bench]
fn bits_rep_u32(b: &mut Bencher) {
b.iter(|| bitarr![u32, LocalBits; 0; 120]);
b.iter(|| bitarr![u32, LocalBits; 1; 120]);
}
#[bench]
#[cfg(target_pointer_width = "64")]
fn bits_rep_u64(b: &mut Bencher) {
b.iter(|| bitarr![u64, LocalBits; 0; 120]);
b.iter(|| bitarr![u64, LocalBits; 1; 120]);
}
#[bench]
fn bitvec_rep(b: &mut Bencher) {
b.iter(|| bitvec![0; 16 * 16 * 9]);
b.iter(|| bitvec![1; 16 * 16 * 9]);
}
#[bench]
fn vec_rep(b: &mut Bencher) {
b.iter(|| vec![0u8; 16 * 16 * 9 / 8]);
b.iter(|| vec![-1i8; 16 * 16 * 9 / 8]);
}
+288
View File
@@ -0,0 +1,288 @@
/*! Benchmarks for `BitSlice::copy_from_slice`.
The `copy_from_slice` implementation attempts to detect slice conditions that
allow element-wise `memcpy` behavior, rather than the conservative bit-by-bit
iteration, as element load/stores are faster than reading and writing each bit
in an element individually.
!*/
#![feature(maybe_uninit_uninit_array, maybe_uninit_slice)]
use std::mem::MaybeUninit;
use bitvec::{
mem::{
bits_of,
elts,
},
prelude::*,
};
use criterion::{
criterion_group,
criterion_main,
BenchmarkId,
Criterion,
SamplingMode,
Throughput,
};
use tap::Tap;
// One kibibyte
const KIBIBYTE: usize = 1024;
// Some number of kibibytes
#[allow(clippy::identity_op)]
const FACTOR: usize = 1 * KIBIBYTE;
// Scalars applied to FACTOR to get a range of action
const SCALARS: &[usize] = &[1, 2, 4, 8, 16, 24, 32, 40, 52, 64];
// The maximum number of bits in a memory region.
const MAX_BITS: usize = 64 * FACTOR * 8;
fn make_slots<T, const LEN: usize>()
-> ([MaybeUninit<T>; LEN], [MaybeUninit<T>; LEN])
where T: BitStore {
(
MaybeUninit::<T>::uninit_array::<LEN>(),
MaybeUninit::<T>::uninit_array::<LEN>(),
)
}
fn view_slots<'a, 'b, T>(
src: &'a [MaybeUninit<T>],
dst: &'b mut [MaybeUninit<T>],
) -> (&'a [T], &'b mut [T])
where
T: BitStore,
{
unsafe {
(
MaybeUninit::slice_assume_init_ref(src),
MaybeUninit::slice_assume_init_mut(dst),
)
}
}
pub fn benchmarks(crit: &mut Criterion) {
fn steps()
-> impl Iterator<Item = (impl Fn(&'static str) -> BenchmarkId, usize, Throughput)>
{
SCALARS.iter().map(|&n| {
(
move |name| BenchmarkId::new(name, n),
n * FACTOR * bits_of::<u8>(),
Throughput::Bytes((n * FACTOR) as u64),
)
})
}
let (src_words, mut dst_words) =
make_slots::<usize, { elts::<usize>(MAX_BITS) }>();
let (src_bytes, mut dst_bytes) =
make_slots::<u8, { elts::<u8>(MAX_BITS) }>();
let (src_words, dst_words) = view_slots(&src_words, &mut dst_words);
let (src_bytes, dst_bytes) = view_slots(&src_bytes, &mut dst_bytes);
macro_rules! mkgrp {
($crit:ident, $name:literal) => {
(&mut *$crit).benchmark_group($name).tap_mut(|grp| {
grp.sampling_mode(SamplingMode::Flat).sample_size(2000);
})
};
}
let mut group = mkgrp!(crit, "Element-wise");
for (id, bits, thrpt) in steps() {
group.throughput(thrpt);
let words = bits / bits_of::<usize>();
let bytes = bits / bits_of::<u8>();
let (src_words, dst_words) =
(&src_words[.. words], &mut dst_words[.. words]);
let (src_bytes, dst_bytes) =
(&src_bytes[.. bytes], &mut dst_bytes[.. bytes]);
// Use the builtin memcpy to run the slices in bulk. This ought to be a
// lower bound on execution time.
group.bench_function(id("words_plain"), |b| {
let (src, dst) = (src_words, &mut *dst_words);
b.iter(|| dst.copy_from_slice(src))
});
group.bench_function(id("bytes_plain"), |b| {
let (src, dst) = (src_bytes, &mut *dst_bytes);
b.iter(|| dst.copy_from_slice(src))
});
group.bench_function(id("words_manual"), |b| {
let (src, dst) = (src_words, &mut *dst_words);
b.iter(|| {
for (from, to) in src.iter().zip(dst.iter_mut()) {
*to = *from;
}
})
});
group.bench_function(id("bytes_manual"), |b| {
let (src, dst) = (src_bytes, &mut *dst_bytes);
b.iter(|| {
for (from, to) in src.iter().zip(dst.iter_mut()) {
*to = *from;
}
})
});
}
group.finish();
let mut group = mkgrp!(crit, "Bit-wise accelerated");
for (id, bits, thrpt) in steps() {
group.throughput(thrpt);
let words = bits / bits_of::<usize>();
let bytes = bits / bits_of::<u8>();
let (src_words, dst_words) =
(&src_words[.. words], &mut dst_words[.. words]);
let (src_bytes, dst_bytes) =
(&src_bytes[.. bytes], &mut dst_bytes[.. bytes]);
// Ideal bitwise memcpy: no edges, same typarams, fully aligned.
group.bench_function(id("bits_words_plain"), |b| {
let (src, dst) = (
src_words.view_bits::<Lsb0>(),
dst_words.view_bits_mut::<Lsb0>(),
);
b.iter(|| dst.copy_from_bitslice(src));
});
group.bench_function(id("bits_bytes_plain"), |b| {
let (src, dst) = (
src_bytes.view_bits::<Lsb0>(),
dst_bytes.view_bits_mut::<Lsb0>(),
);
b.iter(|| dst.copy_from_bitslice(src));
});
// Same typarams, fully aligned, with fuzzed edges.
group.bench_function(id("bits_words_edges"), |b| {
let src = src_words.view_bits::<Lsb0>();
let len = src.len();
let (src, dst) = (
&src[10 .. len - 10],
&mut dst_words.view_bits_mut::<Lsb0>()[10 .. len - 10],
);
b.iter(|| dst.copy_from_bitslice(src));
});
group.bench_function(id("bits_bytes_edges"), |b| {
let src = src_bytes.view_bits::<Lsb0>();
let len = src.len();
let (src, dst) = (
&src[10 .. len - 10],
&mut dst_bytes.view_bits_mut::<Lsb0>()[10 .. len - 10],
);
b.iter(|| dst.copy_from_bitslice(src));
});
// Same typarams, misaligned.
group.bench_function(id("bits_words_misalign"), |b| {
let src = &src_words.view_bits::<Lsb0>()[10 ..];
let dst = &mut dst_words.view_bits_mut::<Lsb0>()[.. src.len()];
b.iter(|| dst.copy_from_bitslice(src));
});
group.bench_function(id("bits_bytes_misalign"), |b| {
let src = &src_bytes.view_bits::<Lsb0>()[10 ..];
let dst = &mut dst_bytes.view_bits_mut::<Lsb0>()[.. src.len()];
b.iter(|| dst.copy_from_bitslice(src));
});
}
group.finish();
let mut group = mkgrp!(crit, "Bit-wise crawl");
for (id, bits, thrpt) in steps() {
group.throughput(thrpt);
let words = bits / bits_of::<usize>();
let bytes = bits / bits_of::<u8>();
let (src_words, dst_words) =
(&src_words[.. words], &mut dst_words[.. words]);
let (src_bytes, dst_bytes) =
(&src_bytes[.. bytes], &mut dst_bytes[.. bytes]);
// Mismatched type parameters
group.bench_function(id("bits_words_mismatched"), |b| {
let (src, dst) = (
src_words.view_bits::<Msb0>(),
dst_words.view_bits_mut::<Lsb0>(),
);
b.iter(|| dst.clone_from_bitslice(src));
});
group.bench_function(id("bits_bytes_mismatched"), |b| {
let (src, dst) = (
src_bytes.view_bits::<Msb0>(),
dst_bytes.view_bits_mut::<Lsb0>(),
);
b.iter(|| dst.clone_from_bitslice(src));
});
// Crawl each bit individually. This ought to be an upper bound on
// execution time.
group.bench_function(id("bitwise_words"), |b| {
let (src, dst) = (
src_words.view_bits::<Lsb0>(),
dst_words.view_bits_mut::<Lsb0>(),
);
b.iter(|| unsafe {
for (from, to) in
src.as_bitptr_range().zip(dst.as_mut_bitptr_range())
{
to.write(from.read());
}
})
});
group.bench_function(id("bitwise_bytes"), |b| {
let (src, dst) = (
src_bytes.view_bits::<Lsb0>(),
dst_bytes.view_bits_mut::<Lsb0>(),
);
b.iter(|| unsafe {
for (from, to) in
src.as_bitptr_range().zip(dst.as_mut_bitptr_range())
{
to.write(from.read());
}
})
});
group.bench_function(id("bitwise_words_mismatch"), |b| {
let (src, dst) = (
src_words.view_bits::<Lsb0>(),
dst_words.view_bits_mut::<Msb0>(),
);
b.iter(|| unsafe {
for (from, to) in
src.as_bitptr_range().zip(dst.as_mut_bitptr_range())
{
to.write(from.read());
}
})
});
group.bench_function(id("bitwise_bytes_mismatch"), |b| {
let (src, dst) = (
src_bytes.view_bits::<Msb0>(),
dst_bytes.view_bits_mut::<Lsb0>(),
);
b.iter(|| unsafe {
for (from, to) in
src.as_bitptr_range().zip(dst.as_mut_bitptr_range())
{
to.write(from.read());
}
})
});
}
}
criterion_group!(benches, benchmarks);
criterion_main!(benches);
+25
View File
@@ -0,0 +1,25 @@
#![feature(test)]
extern crate test;
use bitvec::prelude::*;
use test::Bencher;
#[bench]
fn iter_mut(b: &mut Bencher) {
let mut bits = bitarr![0; 500];
b.iter(|| bits.iter_mut().for_each(|mut b| *b = !*b));
}
#[bench]
#[allow(deprecated)]
fn native_for_each(b: &mut Bencher) {
let mut bits = bitarr![0; 500];
b.iter(|| bits.for_each(|_, b| !b));
}
#[bench]
fn copy_within(b: &mut Bencher) {
let mut a = bitarr![1, 1, 1, 1, 0, 0, 0, 0];
b.iter(|| a.copy_within(.. 4, 2));
}
+139
View File
@@ -0,0 +1,139 @@
#![feature(test)]
extern crate test;
use bitvec::prelude::*;
use test::{
bench::black_box,
Bencher,
};
/* `BitSlice::empty` is not benched, because the compiler const-folds it. It
is not a `const fn`, but it has exactly one function call, which is `const`, and
creates a value object from that function. As such, the compiler can prove that
the return value is a `const` value, and insert the value at all
`BitSlice::empty` call sites. It takes 0ns.
*/
#[bench]
fn element(b: &mut Bencher) {
b.iter(|| BitSlice::<u8, Msb0>::from_element(&!0));
b.iter(|| BitSlice::<u8, Lsb0>::from_element(&!0));
b.iter(|| BitSlice::<u16, Msb0>::from_element(&!0));
b.iter(|| BitSlice::<u16, Lsb0>::from_element(&!0));
b.iter(|| BitSlice::<u32, Msb0>::from_element(&!0));
b.iter(|| BitSlice::<u32, Lsb0>::from_element(&!0));
#[cfg(target_pointer_width = "64")]
{
b.iter(|| BitSlice::<u64, Msb0>::from_element(&!0));
b.iter(|| BitSlice::<u64, Lsb0>::from_element(&!0));
}
}
#[bench]
fn slice(b: &mut Bencher) {
b.iter(|| BitSlice::<u8, Msb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
b.iter(|| BitSlice::<u8, Lsb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
b.iter(|| BitSlice::<u16, Msb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
b.iter(|| BitSlice::<u16, Lsb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
b.iter(|| BitSlice::<u32, Msb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
b.iter(|| BitSlice::<u32, Lsb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
#[cfg(target_pointer_width = "64")]
{
b.iter(|| BitSlice::<u64, Msb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
b.iter(|| BitSlice::<u64, Lsb0>::from_slice(&[0, 1, !0 - 1, !0][..]));
}
}
#[bench]
fn len(b: &mut Bencher) {
let bsb08 = [0u8; 16].view_bits::<Msb0>();
let bsl08 = [0u8; 16].view_bits::<Lsb0>();
b.iter(|| bsb08.len());
b.iter(|| bsl08.len());
let bsb16 = [0u16; 8].view_bits::<Msb0>();
let bsl16 = [0u16; 8].view_bits::<Lsb0>();
b.iter(|| bsb16.len());
b.iter(|| bsl16.len());
let bsb32 = [0u32; 4].view_bits::<Msb0>();
let bsl32 = [0u32; 4].view_bits::<Lsb0>();
b.iter(|| bsb32.len());
b.iter(|| bsl32.len());
#[cfg(target_pointer_width = "64")]
{
let bsb64 = [0u64; 2].view_bits::<Msb0>();
let bsl64 = [0u64; 2].view_bits::<Lsb0>();
b.iter(|| bsb64.len());
b.iter(|| bsl64.len());
}
}
// This index value is not only "nice", it also ensures that the hard path is
// hit in `BitIdx::offset`.
#[bench]
fn index(b: &mut Bencher) {
let bsb08 = [0u8; 16].view_bits::<Msb0>();
let bsl08 = [0u8; 16].view_bits::<Lsb0>();
b.iter(|| assert!(!black_box(bsb08)[black_box(69)]));
b.iter(|| assert!(!black_box(bsl08)[black_box(69)]));
let bsb16 = [0u16; 8].view_bits::<Msb0>();
let bsl16 = [0u16; 8].view_bits::<Lsb0>();
b.iter(|| assert!(!black_box(bsb16)[black_box(69)]));
b.iter(|| assert!(!black_box(bsl16)[black_box(69)]));
let bsb32 = [0u32; 4].view_bits::<Msb0>();
let bsl32 = [0u32; 4].view_bits::<Lsb0>();
b.iter(|| assert!(!black_box(bsb32)[black_box(69)]));
b.iter(|| assert!(!black_box(bsl32)[black_box(69)]));
#[cfg(target_pointer_width = "64")]
{
let bsb64 = [0u64; 2].view_bits::<Msb0>();
let bsl64 = [0u64; 2].view_bits::<Lsb0>();
b.iter(|| assert!(!black_box(bsb64)[black_box(69)]));
b.iter(|| assert!(!black_box(bsl64)[black_box(69)]));
}
}
/* This routine has more work to do: index, create a reference struct, and drop
it. The compiler *should* be able to properly arrange immediate drops, though.
*/
#[bench]
fn get_mut(b: &mut Bencher) {
let mut src = [0u8; 16];
let bsb08 = src.view_bits_mut::<Msb0>();
b.iter(|| *bsb08.get_mut(69).unwrap() = true);
let mut src = [0u8; 16];
let bsl08 = src.view_bits_mut::<Lsb0>();
b.iter(|| *bsl08.get_mut(69).unwrap() = true);
let mut src = [0u16; 8];
let bsb16 = src.view_bits_mut::<Msb0>();
b.iter(|| *bsb16.get_mut(69).unwrap() = true);
let mut src = [0u16; 8];
let bsl16 = src.view_bits_mut::<Lsb0>();
b.iter(|| *bsl16.get_mut(69).unwrap() = true);
let mut src = [0u32; 4];
let bsb32 = src.view_bits_mut::<Msb0>();
b.iter(|| *bsb32.get_mut(69).unwrap() = true);
let mut src = [0u32; 4];
let bsl32 = src.view_bits_mut::<Lsb0>();
b.iter(|| *bsl32.get_mut(69).unwrap() = true);
#[cfg(target_pointer_width = "64")]
{
let mut src = [0u64; 2];
let bsb64 = src.view_bits_mut::<Msb0>();
b.iter(|| *bsb64.get_mut(69).unwrap() = true);
let mut src = [0u64; 2];
let bsl64 = src.view_bits_mut::<Lsb0>();
b.iter(|| *bsl64.get_mut(69).unwrap() = true);
}
}