Skip to content

Framed/stream grants clone the queue handle, costing a refcount per grant #130

Description

@cf-rhett

Summary

Framed (and stream) grants store an owned handle, so every grant clones the BbqHandle::Target. With Arc<BBQueue<..>> that is an atomic refcount increment on acquire and a decrement on release. In an SPSC setup the producer and consumer live on different threads, so those writes land on a cacheline both cores are touching.

The clone does not appear to buy anything: FramedProducer already holds a handle for its entire lifetime, so the queue is guaranteed alive for any grant it hands out. A grant that borrowed the producer's handle would be equally sound and free.

Where

FramedGrantW / FramedGrantR hold bbq: Q::Target:

https://github.com/jamesmunns/bbqueue/blob/main/source/bbq2/src/prod_cons/framed.rs#L63-L88

and the grant constructors fill it by cloning:

Ok(FramedGrantW {
    bbq: self.bbq.clone(),
    ...
})

BbqHandle for Arc<BBQueue<..>> implements bbq_ref as self.clone(), whereas the &BBQueue impl returns *self, which is why a static-backed queue pays nothing here.

Measurement

Ring of 512 KiB with BoxedSlice storage, AtomicCoord, and the maitake notifier, 8-byte (usize) length headers, producer and consumer on separate tokio worker threads, grants sized to a full 64 KiB MTU and committed down to the packet size (mirroring a tun driver loop). 2048 packets per iteration, criterion.

packet size Arc<BBQueue> &'static BBQueue delta
64 B 4.8 M pkt/s 6.3 M pkt/s 50 ns/packet
1500 B 3.6 M pkt/s 4.0 M pkt/s 27 ns/packet
9000 B 2.7 M pkt/s 2.9 M pkt/s 37 ns/packet

The delta is largest at 64 B, where there is no backpressure and the grant rate is highest.

To confirm the refcount is the whole story rather than the Arc indirection, I added a third handle whose Target is a Copy pointer wrapper, changing nothing else. It recovered the entire gap, landing on the &'static numbers with the same heap storage.

Possible direction

A lifetime-parameterised grant that borrows the producer/consumer's handle rather than owning a clone, e.g. FramedGrantW<'a, Q, H> holding &'a Q::Target. That would keep Arc-backed queues free of per-grant refcount traffic while leaving the existing owned-handle behaviour available for callers that need a detached 'static grant.

Happy to put up a PR if that shape sounds reasonable, or if you would rather it be an additional API than a change to the existing one.

Context

Found while moving a userspace VPN data plane off a private bbqueue fork onto 0.7. Not urgent for us -- the wake path costs ~4 us per packet, so 30 ns is noise at our packet rates -- but the queue itself is fast enough that the refcount is measurable, so it seemed worth reporting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions