1 // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4 #![cfg(feature = "backend-mmap")]
5 #![allow(clippy::undocumented_unsafe_blocks)]
6
7 extern crate criterion;
8 extern crate vm_memory;
9
10 use std::fs::{File, OpenOptions};
11 use std::io::Cursor;
12 use std::mem::size_of;
13 use std::path::Path;
14
15 use criterion::{black_box, Criterion};
16
17 use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory};
18
19 const REGION_SIZE: usize = 0x8000_0000;
20 const REGIONS_COUNT: u64 = 8;
21 const ACCESS_SIZE: usize = 0x200;
22
23 #[repr(C)]
24 #[derive(Copy, Clone, Default)]
25 struct SmallDummy {
26 a: u32,
27 b: u32,
28 }
29 unsafe impl ByteValued for SmallDummy {}
30
31 #[repr(C)]
32 #[derive(Copy, Clone, Default)]
33 struct BigDummy {
34 elements: [u64; 12],
35 }
36
37 unsafe impl ByteValued for BigDummy {}
38
make_image(size: usize) -> Vec<u8>39 fn make_image(size: usize) -> Vec<u8> {
40 let mut image: Vec<u8> = Vec::with_capacity(size);
41 for i in 0..size {
42 // We just want some different numbers here, so the conversion is OK.
43 image.push(i as u8);
44 }
45 image
46 }
47
48 enum AccessKind {
49 // The parameter represents the index of the region where the access should happen.
50 // Indices are 0-based.
51 InRegion(u64),
52 // The parameter represents the index of the first region (i.e. where the access starts).
53 CrossRegion(u64),
54 }
55
56 impl AccessKind {
make_offset(&self, access_size: usize) -> u6457 fn make_offset(&self, access_size: usize) -> u64 {
58 match *self {
59 AccessKind::InRegion(idx) => REGION_SIZE as u64 * idx,
60 AccessKind::CrossRegion(idx) => {
61 REGION_SIZE as u64 * (idx + 1) - (access_size as u64 / 2)
62 }
63 }
64 }
65 }
66
benchmark_for_mmap(c: &mut Criterion)67 pub fn benchmark_for_mmap(c: &mut Criterion) {
68 let memory = super::create_guest_memory_mmap(REGION_SIZE, REGIONS_COUNT);
69
70 // Just a sanity check.
71 assert_eq!(
72 memory.last_addr(),
73 GuestAddress(REGION_SIZE as u64 * REGIONS_COUNT - 0x01)
74 );
75
76 let some_small_dummy = SmallDummy {
77 a: 0x1111_2222,
78 b: 0x3333_4444,
79 };
80
81 let some_big_dummy = BigDummy {
82 elements: [0x1111_2222_3333_4444; 12],
83 };
84
85 let mut image = make_image(ACCESS_SIZE);
86 let buf = &mut [0u8; ACCESS_SIZE];
87 let mut file = File::open(Path::new("/dev/zero")).expect("Could not open /dev/zero");
88 let mut file_to_write = OpenOptions::new()
89 .write(true)
90 .open("/dev/null")
91 .expect("Could not open /dev/null");
92
93 let accesses = &[
94 AccessKind::InRegion(0),
95 AccessKind::CrossRegion(0),
96 AccessKind::CrossRegion(REGIONS_COUNT - 2),
97 AccessKind::InRegion(REGIONS_COUNT - 1),
98 ];
99
100 for access in accesses {
101 let offset = access.make_offset(ACCESS_SIZE);
102 let address = GuestAddress(offset);
103
104 // Check performance for read operations.
105 c.bench_function(format!("read_from_{:#0X}", offset).as_str(), |b| {
106 b.iter(|| {
107 black_box(&memory)
108 .read_from(address, &mut Cursor::new(&image), ACCESS_SIZE)
109 .unwrap()
110 })
111 });
112
113 c.bench_function(format!("read_from_file_{:#0X}", offset).as_str(), |b| {
114 b.iter(|| {
115 black_box(&memory)
116 .read_from(address, &mut file, ACCESS_SIZE)
117 .unwrap()
118 })
119 });
120
121 c.bench_function(format!("read_exact_from_{:#0X}", offset).as_str(), |b| {
122 b.iter(|| {
123 black_box(&memory)
124 .read_exact_from(address, &mut Cursor::new(&mut image), ACCESS_SIZE)
125 .unwrap()
126 })
127 });
128
129 c.bench_function(
130 format!("read_entire_slice_from_{:#0X}", offset).as_str(),
131 |b| b.iter(|| black_box(&memory).read_slice(buf, address).unwrap()),
132 );
133
134 c.bench_function(format!("read_slice_from_{:#0X}", offset).as_str(), |b| {
135 b.iter(|| black_box(&memory).read(buf, address).unwrap())
136 });
137
138 let obj_off = access.make_offset(size_of::<SmallDummy>());
139 let obj_addr = GuestAddress(obj_off);
140
141 c.bench_function(
142 format!("read_small_obj_from_{:#0X}", obj_off).as_str(),
143 |b| b.iter(|| black_box(&memory).read_obj::<SmallDummy>(obj_addr).unwrap()),
144 );
145
146 let obj_off = access.make_offset(size_of::<BigDummy>());
147 let obj_addr = GuestAddress(obj_off);
148
149 c.bench_function(format!("read_big_obj_from_{:#0X}", obj_off).as_str(), |b| {
150 b.iter(|| black_box(&memory).read_obj::<BigDummy>(obj_addr).unwrap())
151 });
152
153 // Check performance for write operations.
154 c.bench_function(format!("write_to_{:#0X}", offset).as_str(), |b| {
155 b.iter(|| {
156 black_box(&memory)
157 .write_to(address, &mut Cursor::new(&mut image), ACCESS_SIZE)
158 .unwrap()
159 })
160 });
161
162 c.bench_function(format!("write_to_file_{:#0X}", offset).as_str(), |b| {
163 b.iter(|| {
164 black_box(&memory)
165 .write_to(address, &mut file_to_write, ACCESS_SIZE)
166 .unwrap()
167 })
168 });
169
170 c.bench_function(format!("write_exact_to_{:#0X}", offset).as_str(), |b| {
171 b.iter(|| {
172 black_box(&memory)
173 .write_all_to(address, &mut Cursor::new(&mut image), ACCESS_SIZE)
174 .unwrap()
175 })
176 });
177
178 c.bench_function(
179 format!("write_entire_slice_to_{:#0X}", offset).as_str(),
180 |b| b.iter(|| black_box(&memory).write_slice(buf, address).unwrap()),
181 );
182
183 c.bench_function(format!("write_slice_to_{:#0X}", offset).as_str(), |b| {
184 b.iter(|| black_box(&memory).write(buf, address).unwrap())
185 });
186
187 let obj_off = access.make_offset(size_of::<SmallDummy>());
188 let obj_addr = GuestAddress(obj_off);
189
190 c.bench_function(
191 format!("write_small_obj_to_{:#0X}", obj_off).as_str(),
192 |b| {
193 b.iter(|| {
194 black_box(&memory)
195 .write_obj::<SmallDummy>(some_small_dummy, obj_addr)
196 .unwrap()
197 })
198 },
199 );
200
201 let obj_off = access.make_offset(size_of::<BigDummy>());
202 let obj_addr = GuestAddress(obj_off);
203
204 c.bench_function(format!("write_big_obj_to_{:#0X}", obj_off).as_str(), |b| {
205 b.iter(|| {
206 black_box(&memory)
207 .write_obj::<BigDummy>(some_big_dummy, obj_addr)
208 .unwrap()
209 })
210 });
211 }
212 }
213