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

Code before:
// Benchmarking cases:
//   * `Queue.pop`
//   * `Queue.add_used`
//   * `DescriptorChain.next_descriptor`

use std::num::Wrapping;

use criterion::{criterion_group, criterion_main, Criterion};
use vm_memory::GuestAddress;
use vmm::devices::virtio::queue::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE};
use vmm::devices::virtio::test_utils::VirtQueue;
use vmm::utilities::test_utils::single_region_mem;

/// Create one chain with n descriptors
fn set_dtable_one_chain(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 | VIRTQ_DESC_F_NEXT,
            (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
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(|| {

Review comment (suggestion):
so this is (address, length, flags, next)? I guess changing this "set" function to instead take a `Descriptor` object would be out of scope here, but can we explain the 65562 / n stuff a bit (e.g. max descriptor chain length is 2¹⁶, and we build a continguous buffer of 2¹⁶ bytes and split into n buffers that are chained together)? Also, I assume the offset of 2048 is because of the actual queue structure being at the lower address range? Let's leave comments about all that, too 

Code after:
// Benchmarking cases:
//   * `Queue.pop`
//   * `Queue.add_used`
//   * `DescriptorChain.next_descriptor`

use std::num::Wrapping;

use criterion::{criterion_group, criterion_main, Criterion};
use vm_memory::GuestAddress;
use vmm::devices::virtio::queue::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE};
use vmm::devices::virtio::test_utils::VirtQueue;
use vmm::utilities::test_utils::single_region_mem;

/// Create one chain with n descriptors
/// 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_one_chain(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 | VIRTQ_DESC_F_NEXT,
            (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 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);