use std::{num::NonZeroUsize, ptr::NonNull, time::Duration};
use brunch::{benches, Bench};
use libc::c_void;
use nix::sys::mman::{madvise, mmap_anonymous, mprotect, munmap, MapFlags, MmapAdvise, ProtFlags};
const K_PAGE_SIZE: usize = 4096;
const K_MAX_VMAS: usize = 64001;
fn map_unmap(pages: usize) {
let length = pages * K_PAGE_SIZE;
let length_nonzero = match NonZeroUsize::new(length) {
Some(v) => v,
None => return, };
let addr = unsafe {
mmap_anonymous(
None,
length_nonzero,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
}
.expect("mmap failed in map_unmap");
unsafe { munmap(addr, length) }.expect("munmap failed in map_unmap");
}
fn map_touch_unmap(pages: usize) {
let length = pages * K_PAGE_SIZE;
let length_nonzero = match NonZeroUsize::new(length) {
Some(v) => v,
None => return,
};
let addr = unsafe {
mmap_anonymous(
None,
length_nonzero,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
}
.expect("mmap failed in map_touch_unmap");
unsafe {
let mut cursor = addr.as_ptr() as *mut u8;
let end = cursor.add(length);
while cursor < end {
*cursor = 42;
cursor = cursor.add(K_PAGE_SIZE);
}
}
unsafe { munmap(addr, length) }.expect("munmap failed in map_touch_unmap");
}
fn map_touch_many(page_count: usize) {
let mut pages_vec = Vec::with_capacity(page_count);
for _ in 0..page_count {
let addr = unsafe {
mmap_anonymous(
None,
NonZeroUsize::new(K_PAGE_SIZE).unwrap(),
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
}
.expect("mmap failed in map_touch_many");
unsafe {
*(addr.as_ptr() as *mut u8) = 42;
}
pages_vec.push(addr);
}
for &addr in &pages_vec {
unsafe { munmap(addr, K_PAGE_SIZE) }.expect("munmap failed in map_touch_many");
}
}
fn page_fault_bench() {
let mut test_pages = K_MAX_VMAS; if test_pages % 2 == 0 {
test_pages -= 1;
}
let region_len = test_pages * K_PAGE_SIZE;
let region_len_nonzero = match NonZeroUsize::new(region_len) {
Some(v) => v,
None => return,
};
let map_flags = MapFlags::MAP_SHARED | MapFlags::MAP_POPULATE | MapFlags::MAP_ANONYMOUS;
let addr = unsafe {
nix::sys::mman::mmap_anonymous(None, region_len_nonzero, ProtFlags::PROT_READ, map_flags)
}
.expect("mmap failed in page_fault_bench");
let map_base = unsafe { NonNull::new_unchecked(addr.as_ptr() as *mut c_void) };
for i in 0..(test_pages / 2) {
let page_addr = unsafe { map_base.as_ptr().add((2 * i + 1) * K_PAGE_SIZE) };
let page_addr_nn = unsafe { NonNull::new_unchecked(page_addr as *mut c_void) };
unsafe { mprotect(page_addr_nn, K_PAGE_SIZE, ProtFlags::PROT_NONE) }
.expect("mprotect failed setting PROT_NONE");
}
let mapped_pages = test_pages / 2 + 1;
let mut cur_page = mapped_pages;
let iterations = 2 * mapped_pages;
for _ in 0..iterations {
if cur_page >= mapped_pages {
unsafe { madvise(map_base, region_len, MmapAdvise::MADV_DONTNEED) }
.expect("madvise(DONTNEED) failed");
cur_page = 0;
}
let read_addr = unsafe { map_base.as_ptr().add((2 * cur_page) * K_PAGE_SIZE) };
let val = unsafe { *(read_addr as *const u8) };
std::hint::black_box(val);
cur_page += 1;
}
unsafe { munmap(map_base, region_len) }.expect("munmap failed at page_fault_bench cleanup");
}
fn main() {
benches!(
inline:
Bench::new("MapUnmap(1 pages)").run(|| {
map_unmap(1);
}),
Bench::new("MapUnmap(256 pages)").run(|| {
map_unmap(256);
}),
Bench::new("MapUnmap(65536 pages)").run(|| {
map_unmap(65536);
}),
Bench::new("MapUnmap(131072 pages)").run(|| {
map_unmap(131072);
}),
Bench::new("MapTouchUnmap(1 pages)").run(|| {
map_touch_unmap(1);
}),
Bench::new("MapTouchUnmap(256 pages)").run(|| {
map_touch_unmap(256);
}),
Bench::new("MapTouchUnmap(65536 pages)").run(|| {
map_touch_unmap(65536);
}),
Bench::new("MapTouchUnmap(131072 pages)").run(|| {
map_touch_unmap(131072);
}),
Bench::new("MapTouchMany(1 pages)").run(|| {
map_touch_many(1);
}),
Bench::new("MapTouchMany(16 pages)").run(|| {
map_touch_many(16);
}),
Bench::new("MapTouchMany(256 pages)").run(|| {
map_touch_many(256);
}),
Bench::new("MapTouchMany(4096 pages)").run(|| {
map_touch_many(4096);
}),
Bench::new("PageFault")
.with_timeout(Duration::from_secs(30))
.run(|| {
page_fault_bench();
}),
);
}