Skip to content

Commit

Permalink
Update to latest dependencies: std LazyLock; Clap
Browse files Browse the repository at this point in the history
* Replace lazy_static with LazyLock
* Replace argh with clap
  • Loading branch information
evanj committed Aug 23, 2024
1 parent 7e6ec38 commit 8d9d534
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 180 deletions.
376 changes: 276 additions & 100 deletions Cargo.lock

Large diffs are not rendered by default.

24 changes: 18 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ license = "MIT"
repository = "https://github.com/evanj/hugepagedemo"
keywords = ["page", "hugepage", "madvise", "thp", "tlb"]
categories = ["command-line-utilities"]
publish = false

[lints.clippy]
cargo = "deny"
nursery = "deny"
pedantic = "deny"
style = "deny"
cast_possible_truncation = { level = "allow", priority = 1 }
cast_precision_loss = { level = "allow", priority = 1 }
cast_sign_loss = { level = "allow", priority = 1 }
missing_errors_doc = { level = "allow", priority = 1 }
missing_panics_doc = { level = "allow", priority = 1 }
multiple-crate-versions = { level = "allow", priority = 1 }
too_many_lines = { level = "allow", priority = 1 }

[profile.release-nativecpu]
inherits = "release"
Expand All @@ -18,14 +32,12 @@ debug = true
#rustflags = ["-C", "target-cpu=native"]

[dependencies]
argh = "0.1.9"
clap = { version="4", features = ["derive"] }
go-parse-duration = "0"
humanunits = {git="https://github.com/evanj/humanunits"}
lazy_static = "1"
humanunits = { git="https://github.com/evanj/humanunits" }
memory-stats = "1"
nix = {version="0", features=["mman"]}
rand = "0"
rand_xoshiro = "0"
nix = { version="0", features=["mman", "feature"] }
rand = { version="0", features = ["small_rng"] }
regex = "1"
strum = { version = "0", features = ["derive"] }
time = { version="0", features=["std"]}
13 changes: 3 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,9 @@ all: aligned_alloc_demo
cargo fmt
cargo test
cargo check
# https://zhauniarovich.com/post/2021/2021-09-pedantic-clippy/#paranoid-clippy
# -D clippy::restriction is way too "safe"/careful
# -D clippy::pedantic too paranoid
# -A clippy::option-if-let-else: I stylistically disagree with this
cargo clippy --all-targets --all-features -- \
-D warnings \
-D clippy::nursery \
-A clippy::option-if-let-else \
-D clippy::cargo

cargo clippy --all-targets --all-features -- -D warnings
cargo verify-project
cargo audit
clang-format -i '-style={BasedOnStyle: Google, ColumnLimit: 100}' *.c

run_native:
Expand Down
7 changes: 4 additions & 3 deletions src/anyos_hugepages.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use nix::unistd::SysconfVar;
#[cfg(any(test, target_os = "linux"))]
use std::sync::LazyLock;

#[cfg(any(test, target_os = "linux"))]
#[derive(PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -37,9 +39,8 @@ impl std::fmt::Display for HugepageSetting {

#[cfg(any(test, target_os = "linux"))]
pub fn parse_hugepage_enabled(input: &[u8]) -> Result<HugepageSetting, String> {
lazy_static! {
static ref RE: regex::bytes::Regex = regex::bytes::Regex::new(r#"\[([^\]]+)\]"#).unwrap();
}
static RE: LazyLock<regex::bytes::Regex> =
LazyLock::new(|| regex::bytes::Regex::new(r"\[([^\]]+)\]").unwrap());

let string_matches = RE.captures(input);
if string_matches.is_none() {
Expand Down
45 changes: 21 additions & 24 deletions src/bin/faultlatency.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
use clap::Parser;
use hugepagedemo::MmapRegion;
use std::{
error::Error,
time::{Duration, Instant},
};
use time::OffsetDateTime;

#[derive(argh::FromArgs)]
/// Control the options for fault latency testing.
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
struct FaultLatencyOptions {
/// probe the page latency at this interval.
#[argh(
option,
default = "Duration::from_secs(1)",
from_str_fn(argh_parse_go_duration)
)]
#[arg(long, default_value = "1s", value_parser(clap_parse_go_duration))]
test_interval: Duration,

/// sleep duration between probing the different page sizes.
// allow(dead_code) for Mac OS X where the option is unused
#[allow(dead_code)]
#[argh(
option,
default = "Duration::from_millis(100)",
from_str_fn(argh_parse_go_duration)
)]
//#[allow(dead_code)]
#[arg(long, default_value = "100ms", value_parser(clap_parse_go_duration))]
sleep_between_page_sizes: Duration,
}

/// Parses a duration using Go's formats, with the signature required by argh.
fn argh_parse_go_duration(s: &str) -> Result<Duration, String> {
/// Parses a duration using Go's formats, with the signature required by clap.
fn clap_parse_go_duration(s: &str) -> Result<Duration, String> {
let result = go_parse_duration::parse_duration(s);
match result {
Err(err) => Err(format!("{err:?}")),
Expand Down Expand Up @@ -80,8 +74,9 @@ fn fault_4kib() -> Result<FaultLatency, nix::errno::Errno> {
))
}

#[allow(clippy::similar_names)]
fn main() -> Result<(), Box<dyn Error>> {
let config: FaultLatencyOptions = argh::from_env();
let config = FaultLatencyOptions::parse();

let mut next = Instant::now() + config.test_interval;
loop {
Expand Down Expand Up @@ -119,6 +114,7 @@ fn main() -> Result<(), Box<dyn Error>> {
mod linux {
use hugepagedemo::MmapRegion;
use nix::sys::mman::MmapAdvise;
use std::ptr::NonNull;
use std::{ffi::c_void, time::Instant};

use crate::FaultLatency;
Expand All @@ -128,7 +124,7 @@ mod linux {
/// Unfortunately, the Linux kernel seems to prefer returning
struct MmapMadviseNoUnmap {
_region: MmapRegion,
aligned_pointer: *mut c_void,
aligned_pointer: NonNull<c_void>,
}

impl MmapMadviseNoUnmap {
Expand All @@ -146,12 +142,13 @@ mod linux {
// This allows consecutive calls to mmap to be contiguous, which MIGHT
// allow the kernel to coalesce them into huge pages? Not sure.
let allocation_end = region.get_mut() as usize + align_rounded_size;
let aligned_pointer =
align_pointer_value_down(ALIGNMENT_2MIB, allocation_end - size) as *mut c_void;
let aligned_pointer_usize =
align_pointer_value_down(ALIGNMENT_2MIB, allocation_end - size);

assert!(region.get_mut() <= aligned_pointer);
assert!(aligned_pointer as usize + size <= allocation_end);
assert!(region.ptr_as_usize() <= aligned_pointer_usize);
assert!(aligned_pointer_usize + size <= allocation_end);

let aligned_pointer = NonNull::new(aligned_pointer_usize as *mut c_void).unwrap();
unsafe {
nix::sys::mman::madvise(aligned_pointer, size, MmapAdvise::MADV_HUGEPAGE)
.expect("BUG: madvise must succeed");
Expand All @@ -163,8 +160,8 @@ mod linux {
})
}

pub(crate) fn get_mut(&mut self) -> *mut c_void {
self.aligned_pointer
const fn as_ptr(&self) -> *mut c_void {
self.aligned_pointer.as_ptr()
}
}

Expand All @@ -181,9 +178,9 @@ mod linux {
const PAGE_2MIB: usize = 2 << 20;

let start = Instant::now();
let mut region = MmapMadviseNoUnmap::new(PAGE_2MIB)?;
let region = MmapMadviseNoUnmap::new(PAGE_2MIB)?;
let mmap_end = Instant::now();
let u64_pointer = region.get_mut().cast::<u64>();
let u64_pointer = region.as_ptr().cast::<u64>();
unsafe {
*u64_pointer = 0x42;
}
Expand Down
3 changes: 2 additions & 1 deletion src/linux_hugepages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::ptr::NonNull;

// See: https://www.kernel.org/doc/Documentation/vm/transhuge.txt
const HUGEPAGE_ENABLED_PATH: &str = "/sys/kernel/mm/transparent_hugepage/enabled";
Expand All @@ -24,7 +25,7 @@ pub fn print_hugepage_setting_on_linux() -> Result<(), Box<dyn Error>> {
pub fn madvise_hugepages_on_linux(slice: &mut [u64]) {
const HUGEPAGE_FLAGS: MmapAdvise = MmapAdvise::MADV_HUGEPAGE;

let slice_pointer = slice.as_mut_ptr().cast::<c_void>();
let slice_pointer = NonNull::new(slice.as_mut_ptr().cast::<c_void>()).unwrap();
let slice_byte_len = slice.len() * 8;
unsafe {
nix::sys::mman::madvise(slice_pointer, slice_byte_len, HUGEPAGE_FLAGS)
Expand Down
51 changes: 26 additions & 25 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use clap::Parser;
use hugepagedemo::MmapOwner;
use memory_stats::memory_stats;
use nix::sys::mman::{MapFlags, ProtFlags};
Expand All @@ -6,14 +7,11 @@ use rand::{distributions::Uniform, RngCore, SeedableRng};
use std::error::Error;
use std::num::NonZeroUsize;
use std::os::raw::c_void;
use std::ptr::NonNull;
use std::slice;
use std::thread::sleep;
use std::time::{Duration, Instant};

#[cfg(any(test, target_os = "linux"))]
#[macro_use]
extern crate lazy_static;

mod anyos_hugepages;
mod mmaputils;
#[cfg(target_os = "linux")]
Expand All @@ -39,19 +37,20 @@ use notlinux_hugepages::read_page_size;

const FILLED: u64 = 0x42;

#[derive(argh::FromArgs)]
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
/// Control the options for the huge page demo.
struct HugePageDemoOptions {
/// disable using mmap with madvise.
#[argh(option, default = "RunMode::All")]
#[arg(long, default_value_t = RunMode::All)]
run_mode: RunMode,

/// sleep for 60 seconds before dropping the mmap, to allow examining the process state.
#[argh(switch)]
#[arg(long)]
sleep_before_drop: bool,
}

#[derive(strum::EnumString, Eq, PartialEq)]
#[derive(strum::Display, strum::EnumString, Eq, PartialEq, Debug, Clone)]
enum RunMode {
All,
VecOnly,
Expand All @@ -64,11 +63,11 @@ fn main() -> Result<(), Box<dyn Error>> {
const TEST_SIZE_BYTES: usize = TEST_SIZE_GIB * 1024 * 1024 * 1024;
const TEST_SIZE_U64: usize = TEST_SIZE_BYTES / 8;

let options: HugePageDemoOptions = argh::from_env();
let options = HugePageDemoOptions::parse();

// the rand book suggests Xoshiro256Plus is fast and pretty good:
// the rand book suggests SmallRng is fast and pretty good:
// https://rust-random.github.io/book/guide-rngs.html
let mut rng = rand_xoshiro::Xoshiro256Plus::from_entropy();
let mut rng = rand::rngs::SmallRng::from_entropy();

let mem_before = memory_stats().unwrap();
if options.run_mode == RunMode::All || options.run_mode == RunMode::VecOnly {
Expand Down Expand Up @@ -228,34 +227,32 @@ impl MmapHugeMadviseAligned {
let align_rounded_size =
NonZeroUsize::new(size + alignment).expect("BUG: alignment and size must be > 0");

let mmap_pointer: *mut c_void;
let mmap_pointer: NonNull<c_void>;
unsafe {
mmap_pointer = nix::sys::mman::mmap(
mmap_pointer = nix::sys::mman::mmap_anonymous(
None,
align_rounded_size,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE | flags,
0,
0,
)?;
}

// Calculate the aligned block, preferring the HIGHEST aligned address,
// since the kernel seems to allocate consecutive allocations downward.
// This allows consecutive calls to mmap to be contiguous, which MIGHT
// allow the kernel to coalesce them into huge pages? Not sure.
let allocation_end = mmap_pointer as usize + align_rounded_size.get();
let aligned_pointer =
align_pointer_value_down(alignment, allocation_end - size) as *mut c_void;
let mmap_pointer_usize = mmap_pointer.as_ptr() as usize;
let allocation_end = mmap_pointer_usize + align_rounded_size.get();
let aligned_pointer_usize = align_pointer_value_down(alignment, allocation_end - size);
// alternative of taking the lowest aligned address
// let aligned_pointer =
// align_pointer_value_up(alignment, mmap_pointer as usize) as *mut c_void;

assert!(mmap_pointer <= aligned_pointer);
assert!(aligned_pointer as usize + size <= allocation_end);
assert!(mmap_pointer_usize <= aligned_pointer_usize);
assert!(aligned_pointer_usize + size <= allocation_end);

let unaligned_below_size = aligned_pointer as usize - mmap_pointer as usize;
let aligned_end = aligned_pointer as usize + size;
let unaligned_below_size = aligned_pointer_usize - mmap_pointer_usize;
let aligned_end = aligned_pointer_usize + size;
let unaligned_above_size = allocation_end - aligned_end;
// println!(
// "mmap_pointer:0x{:x} - unaligned_below_end:0x{:x}; aligned_pointer:0x{:x} - aligned_end:0x{:x}; unaligned_above_end:0x{:x}",
Expand All @@ -267,8 +264,8 @@ impl MmapHugeMadviseAligned {
// );

// if there is an unused section BELOW the allocation: unmap it
if aligned_pointer != mmap_pointer {
let unaligned_size = aligned_pointer as usize - mmap_pointer as usize;
if aligned_pointer_usize != mmap_pointer_usize {
let unaligned_size = aligned_pointer_usize - mmap_pointer_usize;
unsafe {
nix::sys::mman::munmap(mmap_pointer, unaligned_size)
.expect("BUG: munmap must succeed");
Expand All @@ -277,8 +274,9 @@ impl MmapHugeMadviseAligned {

// if there is an unused section ABOVE the allocation: unmap it
if unaligned_above_size != 0 {
let aligned_end_pointer = NonNull::new(aligned_end as *mut c_void).unwrap();
unsafe {
nix::sys::mman::munmap(aligned_end as *mut c_void, unaligned_above_size)
nix::sys::mman::munmap(aligned_end_pointer, unaligned_above_size)
.expect("BUG: munmap must succeed");
}
}
Expand All @@ -288,6 +286,7 @@ impl MmapHugeMadviseAligned {
align_rounded_size.get()
);

let aligned_pointer = NonNull::new(aligned_pointer_usize as *mut c_void).unwrap();
Ok(Self {
region: MmapOwner::new(aligned_pointer, size),
})
Expand Down Expand Up @@ -454,5 +453,7 @@ mod test {

v.push(aligned_alloc);
}
// explicitly drop v: makes clippy happy because v is now used
drop(v);
}
}
Loading

0 comments on commit 8d9d534

Please sign in to comment.