Repository: firecracker-microvm/firecracker
File: src/vmm/benches/queue.rs (Rust)

Code before:
            (i + 1) as u16,
        );
    }
    rxq.dtable[n - 1].flags.set(VIRTQ_DESC_F_WRITE);
    rxq.dtable[n - 1].next.set(0);
    rxq.avail.ring[0].set(0);
    rxq.avail.idx.set(n as u16);
}

/// Create n chains with 1 descriptors each
/// Descriptor buffers will leave with offest of 2048 bytes
/// to leave some room for queue objects themselfs.
/// Even though we don't really care about size of descriptors
/// the maximum size of the packet is 64K and we are
/// building a set of descriptors with this total size.
/// For this we need each descriptor to be 64K / n size.
fn set_dtable_many_chains(rxq: &VirtQueue, n: usize) {
    for i in 0..n {
        rxq.dtable[i].set(
            (2048 + (65562 / n) * i) as u64,
            (65562 / n) as u32,
            VIRTQ_DESC_F_WRITE,
            0,
        );
        rxq.avail.ring[i].set(i as u16);
    }
    rxq.avail.idx.set(n as u16);
}

pub fn queue_benchmark(c: &mut Criterion) {
    let mem = single_region_mem(2 * 65562);
    let rxq = VirtQueue::new(GuestAddress(0), &mem, 256);
    let mut queue = rxq.create_queue();

    set_dtable_one_chain(&rxq, 1);
    c.bench_function("next_descriptor_1", |b| {
        b.iter(|| {
            queue.next_avail = Wrapping(0);
            // SAFETY: queue has 1 desc chanin
            let desc = unsafe { queue.pop(&mem).unwrap_unchecked() };
            let mut head = Some(desc);
            while let Some(d) = head {
                head = d.next_descriptor();
            }
        })
    });

    set_dtable_one_chain(&rxq, 2);
    c.bench_function("next_descriptor_2", |b| {
        b.iter(|| {
            queue.next_avail = Wrapping(0);

Review comment (suggestion):
If I understand this correctly, you are saying that a single packet will be distributed across different `DescriptorChain` objects? If that's what you mean, I don't think this is possible from the VirtIO spec. Even if it was, TAP device doesn't work like that. Every write in the TAP device should be a single Ethernet frame. Also every read returns exactly one ethernet frame.

Code after:
        );
    }
    rxq.dtable[n - 1].flags.set(VIRTQ_DESC_F_WRITE);
    rxq.dtable[n - 1].next.set(0);
    rxq.avail.ring[0].set(0);
    rxq.avail.idx.set(n as u16);
}

/// Create n chains with 1 descriptors each
/// Descriptor buffers will leave at the offset of 2048 bytes
/// to leave some room for queue objects.
/// We don't really care about sizes of descriptors,
/// so pick 1024.
fn set_dtable_many_chains(rxq: &VirtQueue, n: usize) {
    let desc_size = 1024;
    for i in 0..n {
        rxq.dtable[i].set(
            (2048 + desc_size * i) as u64,
            desc_size as u32,
            VIRTQ_DESC_F_WRITE,
            0,
        );
        rxq.avail.ring[i].set(i as u16);
    }
    rxq.avail.idx.set(n as u16);
}

pub fn queue_benchmark(c: &mut Criterion) {
    let mem = single_region_mem(65562);
    let rxq = VirtQueue::new(GuestAddress(0), &mem, 256);
    let mut queue = rxq.create_queue();

    set_dtable_one_chain(&rxq, 1);
    queue.next_avail = Wrapping(0);
    let desc = queue.pop(&mem).unwrap();
    c.bench_function("next_descriptor_1", |b| {
        b.iter(|| {
            let mut head = Some(desc.clone());
            while let Some(d) = head {
                head = std::hint::black_box(d.next_descriptor());
            }
        })
    });

    set_dtable_one_chain(&rxq, 2);
    queue.next_avail = Wrapping(0);
    let desc = queue.pop(&mem).unwrap();
    c.bench_function("next_descriptor_2", |b| {
        b.iter(|| {
            let mut head = Some(desc.clone());
            while let Some(d) = head {