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