blob: ed15e18f88482a1f453000e4dd90c6785bc125fe [file] [log] [blame]
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
#![cfg(feature = "backend-mmap")]
#![allow(clippy::undocumented_unsafe_blocks)]
extern crate criterion;
extern crate vm_memory;
use std::fs::{File, OpenOptions};
use std::io::Cursor;
use std::mem::size_of;
use std::path::Path;
use criterion::{black_box, Criterion};
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory};
const REGION_SIZE: usize = 0x8000_0000;
const REGIONS_COUNT: u64 = 8;
const ACCESS_SIZE: usize = 0x200;
#[repr(C)]
#[derive(Copy, Clone, Default)]
struct SmallDummy {
a: u32,
b: u32,
}
unsafe impl ByteValued for SmallDummy {}
#[repr(C)]
#[derive(Copy, Clone, Default)]
struct BigDummy {
elements: [u64; 12],
}
unsafe impl ByteValued for BigDummy {}
fn make_image(size: usize) -> Vec<u8> {
let mut image: Vec<u8> = Vec::with_capacity(size);
for i in 0..size {
// We just want some different numbers here, so the conversion is OK.
image.push(i as u8);
}
image
}
enum AccessKind {
// The parameter represents the index of the region where the access should happen.
// Indices are 0-based.
InRegion(u64),
// The parameter represents the index of the first region (i.e. where the access starts).
CrossRegion(u64),
}
impl AccessKind {
fn make_offset(&self, access_size: usize) -> u64 {
match *self {
AccessKind::InRegion(idx) => REGION_SIZE as u64 * idx,
AccessKind::CrossRegion(idx) => {
REGION_SIZE as u64 * (idx + 1) - (access_size as u64 / 2)
}
}
}
}
pub fn benchmark_for_mmap(c: &mut Criterion) {
let memory = super::create_guest_memory_mmap(REGION_SIZE, REGIONS_COUNT);
// Just a sanity check.
assert_eq!(
memory.last_addr(),
GuestAddress(REGION_SIZE as u64 * REGIONS_COUNT - 0x01)
);
let some_small_dummy = SmallDummy {
a: 0x1111_2222,
b: 0x3333_4444,
};
let some_big_dummy = BigDummy {
elements: [0x1111_2222_3333_4444; 12],
};
let mut image = make_image(ACCESS_SIZE);
let buf = &mut [0u8; ACCESS_SIZE];
let mut file = File::open(Path::new("/dev/zero")).expect("Could not open /dev/zero");
let mut file_to_write = OpenOptions::new()
.write(true)
.open("/dev/null")
.expect("Could not open /dev/null");
let accesses = &[
AccessKind::InRegion(0),
AccessKind::CrossRegion(0),
AccessKind::CrossRegion(REGIONS_COUNT - 2),
AccessKind::InRegion(REGIONS_COUNT - 1),
];
for access in accesses {
let offset = access.make_offset(ACCESS_SIZE);
let address = GuestAddress(offset);
// Check performance for read operations.
c.bench_function(format!("read_from_{:#0X}", offset).as_str(), |b| {
b.iter(|| {
black_box(&memory)
.read_from(address, &mut Cursor::new(&image), ACCESS_SIZE)
.unwrap()
})
});
c.bench_function(format!("read_from_file_{:#0X}", offset).as_str(), |b| {
b.iter(|| {
black_box(&memory)
.read_from(address, &mut file, ACCESS_SIZE)
.unwrap()
})
});
c.bench_function(format!("read_exact_from_{:#0X}", offset).as_str(), |b| {
b.iter(|| {
black_box(&memory)
.read_exact_from(address, &mut Cursor::new(&mut image), ACCESS_SIZE)
.unwrap()
})
});
c.bench_function(
format!("read_entire_slice_from_{:#0X}", offset).as_str(),
|b| b.iter(|| black_box(&memory).read_slice(buf, address).unwrap()),
);
c.bench_function(format!("read_slice_from_{:#0X}", offset).as_str(), |b| {
b.iter(|| black_box(&memory).read(buf, address).unwrap())
});
let obj_off = access.make_offset(size_of::<SmallDummy>());
let obj_addr = GuestAddress(obj_off);
c.bench_function(
format!("read_small_obj_from_{:#0X}", obj_off).as_str(),
|b| b.iter(|| black_box(&memory).read_obj::<SmallDummy>(obj_addr).unwrap()),
);
let obj_off = access.make_offset(size_of::<BigDummy>());
let obj_addr = GuestAddress(obj_off);
c.bench_function(format!("read_big_obj_from_{:#0X}", obj_off).as_str(), |b| {
b.iter(|| black_box(&memory).read_obj::<BigDummy>(obj_addr).unwrap())
});
// Check performance for write operations.
c.bench_function(format!("write_to_{:#0X}", offset).as_str(), |b| {
b.iter(|| {
black_box(&memory)
.write_to(address, &mut Cursor::new(&mut image), ACCESS_SIZE)
.unwrap()
})
});
c.bench_function(format!("write_to_file_{:#0X}", offset).as_str(), |b| {
b.iter(|| {
black_box(&memory)
.write_to(address, &mut file_to_write, ACCESS_SIZE)
.unwrap()
})
});
c.bench_function(format!("write_exact_to_{:#0X}", offset).as_str(), |b| {
b.iter(|| {
black_box(&memory)
.write_all_to(address, &mut Cursor::new(&mut image), ACCESS_SIZE)
.unwrap()
})
});
c.bench_function(
format!("write_entire_slice_to_{:#0X}", offset).as_str(),
|b| b.iter(|| black_box(&memory).write_slice(buf, address).unwrap()),
);
c.bench_function(format!("write_slice_to_{:#0X}", offset).as_str(), |b| {
b.iter(|| black_box(&memory).write(buf, address).unwrap())
});
let obj_off = access.make_offset(size_of::<SmallDummy>());
let obj_addr = GuestAddress(obj_off);
c.bench_function(
format!("write_small_obj_to_{:#0X}", obj_off).as_str(),
|b| {
b.iter(|| {
black_box(&memory)
.write_obj::<SmallDummy>(some_small_dummy, obj_addr)
.unwrap()
})
},
);
let obj_off = access.make_offset(size_of::<BigDummy>());
let obj_addr = GuestAddress(obj_off);
c.bench_function(format!("write_big_obj_to_{:#0X}", obj_off).as_str(), |b| {
b.iter(|| {
black_box(&memory)
.write_obj::<BigDummy>(some_big_dummy, obj_addr)
.unwrap()
})
});
}
}