Skip to content

Commit f79782b

Browse files
committed
feat(bench): add queue benches
Add benchmarks for `Queue` and `DesciptorChain` objects. This will allows us to see performance difference with changes introduced to the queue in the future. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent d6bbb22 commit f79782b

File tree

3 files changed

+199
-8
lines changed

3 files changed

+199
-8
lines changed

src/vmm/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,9 @@ tracing = ["log-instrument"]
6161
name = "cpu_templates"
6262
harness = false
6363

64+
[[bench]]
65+
name = "queue"
66+
harness = false
67+
6468
[lints]
6569
workspace = true

src/vmm/benches/queue.rs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Benchmarking cases:
5+
// * `Queue.pop`
6+
// * `Queue.add_used`
7+
// * `DescriptorChain.next_descriptor`
8+
9+
use std::num::Wrapping;
10+
11+
use criterion::{criterion_group, criterion_main, Criterion};
12+
use vm_memory::GuestAddress;
13+
use vmm::devices::virtio::queue::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE};
14+
use vmm::devices::virtio::test_utils::VirtQueue;
15+
use vmm::utilities::test_utils::single_region_mem;
16+
17+
/// Create one chain with n descriptors
18+
/// Descriptor buffers will leave at the offset of 2048 bytes
19+
/// to leave some room for queue objects.
20+
/// We don't really care about sizes of descriptors,
21+
/// so pick 1024.
22+
fn set_dtable_one_chain(rxq: &VirtQueue, n: usize) {
23+
let desc_size = 1024;
24+
for i in 0..n {
25+
rxq.dtable[i].set(
26+
(2048 + desc_size * i) as u64,
27+
desc_size as u32,
28+
VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT,
29+
(i + 1) as u16,
30+
);
31+
}
32+
rxq.dtable[n - 1].flags.set(VIRTQ_DESC_F_WRITE);
33+
rxq.dtable[n - 1].next.set(0);
34+
rxq.avail.ring[0].set(0);
35+
rxq.avail.idx.set(n as u16);
36+
}
37+
38+
/// Create n chains with 1 descriptors each
39+
/// Descriptor buffers will leave at the offset of 2048 bytes
40+
/// to leave some room for queue objects.
41+
/// We don't really care about sizes of descriptors,
42+
/// so pick 1024.
43+
fn set_dtable_many_chains(rxq: &VirtQueue, n: usize) {
44+
let desc_size = 1024;
45+
for i in 0..n {
46+
rxq.dtable[i].set(
47+
(2048 + desc_size * i) as u64,
48+
desc_size as u32,
49+
VIRTQ_DESC_F_WRITE,
50+
0,
51+
);
52+
rxq.avail.ring[i].set(i as u16);
53+
}
54+
rxq.avail.idx.set(n as u16);
55+
}
56+
57+
pub fn queue_benchmark(c: &mut Criterion) {
58+
let mem = single_region_mem(65562);
59+
let rxq = VirtQueue::new(GuestAddress(0), &mem, 256);
60+
let mut queue = rxq.create_queue();
61+
62+
set_dtable_one_chain(&rxq, 1);
63+
queue.next_avail = Wrapping(0);
64+
let desc = queue.pop(&mem).unwrap();
65+
c.bench_function("next_descriptor_1", |b| {
66+
b.iter(|| {
67+
let mut head = Some(desc.clone());
68+
while let Some(d) = head {
69+
head = std::hint::black_box(d.next_descriptor());
70+
}
71+
})
72+
});
73+
74+
set_dtable_one_chain(&rxq, 2);
75+
queue.next_avail = Wrapping(0);
76+
let desc = queue.pop(&mem).unwrap();
77+
c.bench_function("next_descriptor_2", |b| {
78+
b.iter(|| {
79+
let mut head = Some(desc.clone());
80+
while let Some(d) = head {
81+
head = std::hint::black_box(d.next_descriptor());
82+
}
83+
})
84+
});
85+
86+
set_dtable_one_chain(&rxq, 4);
87+
queue.next_avail = Wrapping(0);
88+
let desc = queue.pop(&mem).unwrap();
89+
c.bench_function("next_descriptor_4", |b| {
90+
b.iter(|| {
91+
let mut head = Some(desc.clone());
92+
while let Some(d) = head {
93+
head = std::hint::black_box(d.next_descriptor());
94+
}
95+
})
96+
});
97+
98+
set_dtable_one_chain(&rxq, 16);
99+
queue.next_avail = Wrapping(0);
100+
let desc = queue.pop(&mem).unwrap();
101+
c.bench_function("next_descriptor_16", |b| {
102+
b.iter(|| {
103+
let mut head = Some(desc.clone());
104+
while let Some(d) = head {
105+
head = std::hint::black_box(d.next_descriptor());
106+
}
107+
})
108+
});
109+
110+
// Queue pop
111+
112+
set_dtable_many_chains(&rxq, 1);
113+
c.bench_function("queue_pop_1", |b| {
114+
b.iter(|| {
115+
queue.next_avail = Wrapping(0);
116+
while let Some(desc) = queue.pop(&mem) {
117+
std::hint::black_box(desc);
118+
}
119+
})
120+
});
121+
122+
set_dtable_many_chains(&rxq, 4);
123+
c.bench_function("queue_pop_4", |b| {
124+
b.iter(|| {
125+
queue.next_avail = Wrapping(0);
126+
while let Some(desc) = queue.pop(&mem) {
127+
std::hint::black_box(desc);
128+
}
129+
})
130+
});
131+
132+
set_dtable_many_chains(&rxq, 16);
133+
c.bench_function("queue_pop_16", |b| {
134+
b.iter(|| {
135+
queue.next_avail = Wrapping(0);
136+
while let Some(desc) = queue.pop(&mem) {
137+
std::hint::black_box(desc);
138+
}
139+
})
140+
});
141+
142+
c.bench_function("queue_add_used_1", |b| {
143+
b.iter(|| {
144+
queue.num_added = Wrapping(0);
145+
queue.next_used = Wrapping(0);
146+
for i in 0_u16..1_u16 {
147+
let index = std::hint::black_box(i);
148+
let len = std::hint::black_box(i + 1);
149+
_ = queue.add_used(&mem, index as u16, len as u32);
150+
}
151+
})
152+
});
153+
154+
c.bench_function("queue_add_used_16", |b| {
155+
b.iter(|| {
156+
queue.num_added = Wrapping(0);
157+
queue.next_used = Wrapping(0);
158+
for i in 0_u16..16_u16 {
159+
let index = std::hint::black_box(i);
160+
let len = std::hint::black_box(i + 1);
161+
_ = queue.add_used(&mem, index as u16, len as u32);
162+
}
163+
})
164+
});
165+
166+
c.bench_function("queue_add_used_256", |b| {
167+
b.iter(|| {
168+
queue.num_added = Wrapping(0);
169+
queue.next_used = Wrapping(0);
170+
for i in 0_u16..256_u16 {
171+
let index = std::hint::black_box(i);
172+
let len = std::hint::black_box(i + 1);
173+
_ = queue.add_used(&mem, index as u16, len as u32);
174+
}
175+
})
176+
});
177+
}
178+
179+
criterion_group! {
180+
name = queue_benches;
181+
config = Criterion::default().sample_size(200).noise_threshold(0.05);
182+
targets = queue_benchmark
183+
}
184+
185+
criterion_main! {
186+
queue_benches
187+
}

src/vmm/src/devices/virtio/queue.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use crate::vstate::memory::{
1616
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap,
1717
};
1818

19-
pub(super) const VIRTQ_DESC_F_NEXT: u16 = 0x1;
20-
pub(super) const VIRTQ_DESC_F_WRITE: u16 = 0x2;
19+
pub const VIRTQ_DESC_F_NEXT: u16 = 0x1;
20+
pub const VIRTQ_DESC_F_WRITE: u16 = 0x2;
2121

2222
/// Max size of virtio queues offered by firecracker's virtio devices.
2323
pub(super) const FIRECRACKER_MAX_QUEUE_SIZE: u16 = 256;
@@ -69,7 +69,7 @@ struct UsedElement {
6969
unsafe impl ByteValued for UsedElement {}
7070

7171
/// A virtio descriptor chain.
72-
#[derive(Debug)]
72+
#[derive(Debug, Copy, Clone)]
7373
pub struct DescriptorChain<'a, M: GuestMemory = GuestMemoryMmap> {
7474
desc_table: GuestAddress,
7575
queue_size: u16,
@@ -205,7 +205,7 @@ impl<'a> Iterator for DescriptorIterator<'a> {
205205
/// A virtio queue's parameters.
206206
pub struct Queue {
207207
/// The maximal size in elements offered by the device
208-
pub(crate) max_size: u16,
208+
pub max_size: u16,
209209

210210
/// The queue size in elements the driver selected
211211
pub size: u16,
@@ -222,13 +222,13 @@ pub struct Queue {
222222
/// Guest physical address of the used ring
223223
pub used_ring: GuestAddress,
224224

225-
pub(crate) next_avail: Wrapping<u16>,
226-
pub(crate) next_used: Wrapping<u16>,
225+
pub next_avail: Wrapping<u16>,
226+
pub next_used: Wrapping<u16>,
227227

228228
/// VIRTIO_F_RING_EVENT_IDX negotiated (notification suppression enabled)
229-
pub(crate) uses_notif_suppression: bool,
229+
pub uses_notif_suppression: bool,
230230
/// The number of added used buffers since last guest kick
231-
pub(crate) num_added: Wrapping<u16>,
231+
pub num_added: Wrapping<u16>,
232232
}
233233

234234
#[allow(clippy::len_without_is_empty)]

0 commit comments

Comments
 (0)