rustzmq2 0.1.0

A native async Rust implementation of ZeroMQ
Documentation
//! Pure codec microbenchmarks — no I/O.
//!
//! Isolates ZMTP encode/decode cost from transport cost so Phase 2 codec
//! changes (iterative decoder, pre-reserved encoder, vectored writes) can
//! be measured without network noise.

use bytes::{Bytes, BytesMut};
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use tokio_util::codec::{Decoder, Encoder};

use rustzmq2::{
    ZmqMessage,
    __bench::{Message, ZmqCodec},
};

const FRAME_SIZES: &[usize] = &[16, 256, 4096, 65536];
const MULTIPART_FRAME_COUNTS: &[usize] = &[1, 2, 8];

fn build_message(frame_count: usize, frame_size: usize) -> ZmqMessage {
    let first = Bytes::from(vec![0xABu8; frame_size]);
    let mut m = ZmqMessage::from(first);
    for _ in 1..frame_count {
        m.push_back(Bytes::from(vec![0xCDu8; frame_size]));
    }
    m
}

fn bench_encode(c: &mut Criterion) {
    let mut group = c.benchmark_group("codec/encode");
    for &frames in MULTIPART_FRAME_COUNTS {
        for &size in FRAME_SIZES {
            let m = build_message(frames, size);
            let total_bytes = (frames * size) as u64;
            group.throughput(Throughput::Bytes(total_bytes));
            group.bench_with_input(
                BenchmarkId::new(format!("frames={}", frames), size),
                &(frames, size),
                |b, _| {
                    b.iter(|| {
                        let mut codec = ZmqCodec::new();
                        let mut dst = BytesMut::with_capacity(total_bytes as usize + 64);
                        codec
                            .encode(Message::Message(m.clone()), &mut dst)
                            .expect("encode");
                        black_box(&dst);
                    });
                },
            );
        }
    }
    group.finish();
}

fn bench_decode(c: &mut Criterion) {
    // Pre-build the encoded form once, then decode it from a fresh copy per iteration.
    let mut group = c.benchmark_group("codec/decode");
    for &frames in MULTIPART_FRAME_COUNTS {
        for &size in FRAME_SIZES {
            let m = build_message(frames, size);
            let total_bytes = (frames * size) as u64;

            // Encode once to get the wire-format bytes.
            let mut encoded = BytesMut::new();
            {
                let mut codec = ZmqCodec::new();
                codec
                    .encode(Message::Message(m), &mut encoded)
                    .expect("encode for decode bench");
            }
            let encoded = encoded.freeze();

            group.throughput(Throughput::Bytes(total_bytes));
            group.bench_with_input(
                BenchmarkId::new(format!("frames={}", frames), size),
                &(frames, size),
                |b, _| {
                    b.iter(|| {
                        let mut codec = ZmqCodec::new();
                        // Skip greeting — decoder starts in Greeting state. Drive it past greeting
                        // by feeding a fake 64-byte greeting so we only benchmark frame decode.
                        // Simplest: construct buffer = greeting || encoded.
                        let mut src = BytesMut::new();
                        src.extend_from_slice(&GREETING_STUB);
                        src.extend_from_slice(&encoded);
                        // Consume the greeting.
                        let _ = codec.decode(&mut src).expect("greeting");
                        // Decode the message.
                        let msg = codec.decode(&mut src).expect("decode").expect("some");
                        black_box(msg);
                    });
                },
            );
        }
    }
    group.finish();
}

// A valid-looking 64-byte ZMTP 3.1 NULL greeting.
const GREETING_STUB: [u8; 64] = {
    let mut g = [0u8; 64];
    g[0] = 0xff; // signature
    g[9] = 0x7f; // end of signature
    g[10] = 3; // version major
    g[11] = 1; // version minor
               // mechanism: "NULL" zero-padded to 20 bytes at offset 12
    g[12] = b'N';
    g[13] = b'U';
    g[14] = b'L';
    g[15] = b'L';
    g
};

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