fashex 0.0.6

Hexadecimal string encoding and decoding with best-effort SIMD acceleration.
Documentation
//! Throughput benchmarks.

#![cfg(any(
    target_arch = "x86",
    target_arch = "x86_64",
    target_arch = "aarch64",
    target_arch = "loongarch64"
))]
#![allow(clippy::pedantic, reason = "XXX")]

use std::hint::black_box;
use std::iter;

use criterion::{
    AxisScale, BenchmarkId, Criterion, PlotConfiguration, Throughput, criterion_group,
    criterion_main,
};
use fastant::Instant;

fn bench_decode(c: &mut Criterion) {
    let mut group = c.benchmark_group("decode/lowercase");

    group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic));

    for size in [1048576, 131072, 16384, 2048, 256, 32] {
        let expected = iter::repeat_n(fastrand::u8(..), size / 2).collect::<Vec<_>>();

        let input = expected
            .iter()
            .copied()
            .flat_map(|b| {
                [
                    fashex::HEX_CHARS_LOWER[(b >> 4) as usize] as char,
                    fashex::HEX_CHARS_LOWER[(b & 0b1111) as usize] as char,
                ]
            })
            .collect::<String>();

        group.throughput(Throughput::Bytes(size as u64));

        group.bench_function(BenchmarkId::new("fashex-const", size), |b| {
            b.iter_custom(|iters| {
                (0..iters)
                    .map(|_| {
                        let mut output = Vec::with_capacity(size / 2);

                        let elapsed = {
                            let output = &mut output.spare_capacity_mut()[..size / 2];

                            let now = Instant::now();

                            fashex::decode_generic(black_box(input.as_bytes()), output).unwrap();

                            now.elapsed()
                        };

                        #[allow(unsafe_code, reason = "XXX")]
                        unsafe {
                            output.set_len(size / 2);
                        }

                        assert!(expected == output);

                        elapsed
                    })
                    .sum()
            });
        });

        group.bench_function(BenchmarkId::new("fashex", size), |b| {
            b.iter_custom(|iters| {
                (0..iters)
                    .map(|_| {
                        let mut output = Vec::with_capacity(size / 2);

                        let elapsed = {
                            let output = &mut output.spare_capacity_mut()[..size / 2];

                            let now = Instant::now();

                            fashex::decode(black_box(input.as_bytes()), output).unwrap();

                            now.elapsed()
                        };

                        #[allow(unsafe_code, reason = "XXX")]
                        unsafe {
                            output.set_len(size / 2);
                        }

                        assert!(expected == output);

                        elapsed
                    })
                    .sum()
            });
        });

        group.bench_function(BenchmarkId::new("hex-simd", size), |b| {
            b.iter_custom(|iters| {
                (0..iters)
                    .map(|_| {
                        let mut output = Vec::with_capacity(size / 2);

                        let elapsed = {
                            let now = Instant::now();

                            hex_simd::decode_append(black_box(&input), &mut output).unwrap();

                            now.elapsed()
                        };

                        assert!(expected == output);

                        elapsed
                    })
                    .sum()
            });
        });

        group.bench_function(BenchmarkId::new("const-hex", size), |b| {
            b.iter_custom(|iters| {
                (0..iters)
                    .map(|_| {
                        let mut output = vec![0; size / 2];

                        let elapsed = {
                            let now = Instant::now();

                            const_hex::decode_to_slice(black_box(&input), &mut output).unwrap();

                            now.elapsed()
                        };

                        assert!(expected == output);

                        elapsed
                    })
                    .sum()
            });
        });

        group.bench_function(BenchmarkId::new("faster-hex", size), |b| {
            b.iter_custom(|iters| {
                (0..iters)
                    .map(|_| {
                        let mut output = vec![0; size / 2];

                        let elapsed = {
                            let now = Instant::now();

                            faster_hex::hex_decode(black_box(input.as_bytes()), &mut output)
                                .unwrap();

                            now.elapsed()
                        };

                        assert!(expected == output);

                        elapsed
                    })
                    .sum()
            });
        });
    }

    group.finish();
}

criterion_group!(benches, bench_decode);
criterion_main!(benches);