From 5b2e9addfe03ff96fd5f6be4374bdfc4e1eef0f5 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Wed, 17 May 2023 23:21:04 +0100 Subject: [PATCH] Added page locations to the HeMem statistics. Added more details to the debug output of HeMem. --- .vscode/settings.json | 1 + Cargo.lock | 49 ++++++++++++ Cargo.toml | 1 + src/classifiers/hemem.rs | 115 +++++++++++++++++++++++----- src/classifiers/hemem/page_table.rs | 2 +- src/classifiers/hemem/statistics.rs | 38 ++++++++- src/main.rs | 2 +- 7 files changed, 186 insertions(+), 22 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2ed4801..aff11fb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "ftmemsim", "hemem", "itertools", + "minmax", "optane", "pico", "PICOS" diff --git a/Cargo.lock b/Cargo.lock index fbdeffc..16f7748 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,23 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "average" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843ec791d3f24503bbf72bbd5e49a3ab4dbb4bcd0a8ef6b0c908efa73caa27b1" +dependencies = [ + "easy-cast", + "float-ord", + "num-traits", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -129,6 +146,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "easy-cast" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd102ee8c418348759919b83b81cdbdc933ffe29740b903df448b4bafaa348e" +dependencies = [ + "libm", +] + [[package]] name = "either" version = "1.8.1" @@ -167,11 +193,18 @@ dependencies = [ "syn", ] +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + [[package]] name = "ftmemsim" version = "0.1.0" dependencies = [ "anyhow", + "average", "byteorder", "clap", "extend", @@ -236,6 +269,12 @@ version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + [[package]] name = "linux-raw-sys" version = "0.3.7" @@ -270,6 +309,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "once_cell" version = "1.17.1" diff --git a/Cargo.toml b/Cargo.toml index fa24e4e..43d8589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0.71" +average = "0.13.1" byteorder = "1.4.3" clap = { version = "4.2.7", features = ["derive"] } extend = "1.2.0" diff --git a/src/classifiers/hemem.rs b/src/classifiers/hemem.rs index 3b7a149..5b89880 100644 --- a/src/classifiers/hemem.rs +++ b/src/classifiers/hemem.rs @@ -17,6 +17,7 @@ use { self::memories::MemIdx, crate::{pin_trace, sim}, anyhow::Context, + itertools::Itertools, std::fmt, }; @@ -54,7 +55,7 @@ impl HeMem { /// /// # Panics /// Panics if the page is already mapped. - pub fn map_page(&mut self, page_ptr: PagePtr) -> Result<(), anyhow::Error> { + pub fn map_page(&mut self, page_ptr: PagePtr) -> Result { if self.page_table.contains(page_ptr) { panic!("Page is already mapped: {page_ptr:?}"); } @@ -66,7 +67,7 @@ impl HeMem { Ok(()) => { let page = Page::new(page_ptr, mem_idx); self.page_table.insert(page).expect("Unable to insert unmapped page"); - return Ok(()); + return Ok(mem_idx); }, // If we didn't manage to, go to the next page @@ -87,10 +88,10 @@ impl HeMem { /// /// # Panics /// Panics if `mem_idx` is an invalid memory index - pub fn cool_memory(&mut self, mem_idx: MemIdx, count: usize) -> usize { + pub fn cool_memory(&mut self, cur_time: u64, mem_idx: MemIdx, count: usize) -> usize { let mut cooled_pages = 0; for page_ptr in self.page_table.coldest_pages(mem_idx, count) { - if self.cool_page(page_ptr).is_ok() { + if self.cool_page(cur_time, page_ptr).is_ok() { cooled_pages += 1; } } @@ -106,13 +107,21 @@ impl HeMem { /// # Panics /// Panics if `page_ptr` isn't a mapped page. /// Panics if `dst_mem_idx` is an invalid memory index. - pub fn migrate_page(&mut self, page_ptr: PagePtr, dst_mem_idx: MemIdx) -> Result<(), anyhow::Error> { + pub fn migrate_page(&mut self, cur_time: u64, page_ptr: PagePtr, dst_mem_idx: MemIdx) -> Result<(), anyhow::Error> { let page = self.page_table.get_mut(page_ptr).expect("Page wasn't in page table"); let src_mem_idx = page.mem_idx(); match self.memories.migrate_page(src_mem_idx, dst_mem_idx) { // If we managed to, move the page's memory - Ok(()) => page.move_mem(dst_mem_idx), + Ok(()) => { + page.move_mem(dst_mem_idx); + + self.statistics + .register_page_location(page_ptr, statistics::PageLocation { + time: cur_time, + mem_idx: dst_mem_idx, + }); + }, // Else try to cool the destination memory first, then try again Err(err) => { @@ -124,12 +133,25 @@ impl HeMem { ); // TODO: Cool for more than just 1 page at a time? - let pages_cooled = self.cool_memory(dst_mem_idx, 1); + let pages_cooled = self.cool_memory(cur_time, dst_mem_idx, 1); match pages_cooled > 0 { - true => self - .memories - .migrate_page(src_mem_idx, dst_mem_idx) - .expect("Just freed some pages when cooling"), + // If we cooled at least 1 page, migrate it + true => { + self.memories + .migrate_page(src_mem_idx, dst_mem_idx) + .expect("Just freed some pages when cooling"); + self.page_table + .get_mut(page_ptr) + .expect("Page wasn't in page table") + .move_mem(dst_mem_idx); + self.statistics + .register_page_location(page_ptr, statistics::PageLocation { + time: cur_time, + mem_idx: dst_mem_idx, + }); + }, + + // Else we can't move it false => anyhow::bail!("Cooler memory is full, even after cooling it"), } }, @@ -145,7 +167,7 @@ impl HeMem { /// /// # Panics /// Panics if `page_ptr` isn't a mapped page. - pub fn cool_page(&mut self, page_ptr: PagePtr) -> Result<(), anyhow::Error> { + pub fn cool_page(&mut self, cur_time: u64, page_ptr: PagePtr) -> Result<(), anyhow::Error> { let page = self.page_table.get_mut(page_ptr).expect("Page wasn't in page table"); // Get the new memory index in the slower memory @@ -155,7 +177,7 @@ impl HeMem { }; // Then try to migrate it - self.migrate_page(page_ptr, dst_mem_idx) + self.migrate_page(cur_time, page_ptr, dst_mem_idx) .context("Unable to migrate page to slower memory") } @@ -166,7 +188,7 @@ impl HeMem { /// /// # Panics /// Panics if `page_ptr` isn't a mapped page. - pub fn warm_page(&mut self, page_ptr: PagePtr) -> Result<(), anyhow::Error> { + pub fn warm_page(&mut self, cur_time: u64, page_ptr: PagePtr) -> Result<(), anyhow::Error> { let page = self.page_table.get_mut(page_ptr).expect("Page wasn't in page table"); // Get the new memory index in the faster memory @@ -177,7 +199,7 @@ impl HeMem { }; // Then try to migrate it - self.migrate_page(page_ptr, dst_mem_idx) + self.migrate_page(cur_time, page_ptr, dst_mem_idx) .context("Unable to migrate page to faster memory") } } @@ -191,7 +213,14 @@ impl sim::Classifier for HeMem { let page_prev_mem_idx = self.page_table.get_mut(page_ptr).map(|page| page.mem_idx()); if !self.page_table.contains(page_ptr) { tracing::trace!(?page_ptr, "Mapping page"); - self.map_page(page_ptr).context("Unable to map page")?; + let page_mem_idx = self.map_page(page_ptr).context("Unable to map page")?; + + // Register an initial page location when mapping + self.statistics + .register_page_location(page_ptr, statistics::PageLocation { + time: trace.record.time, + mem_idx: page_mem_idx, + }); }; let page = self.page_table.get_mut(page_ptr).expect("Page wasn't in page table"); let page_was_hot = page.is_hot(self.config.read_hot_threshold, self.config.write_hot_threshold); @@ -218,7 +247,7 @@ impl sim::Classifier for HeMem { // If the page isn't hot and it was hot, cool it if !page_is_hot && page_was_hot { tracing::trace!(?page_ptr, "Page is no longer hot, cooling it"); - if let Err(err) = self.cool_page(page_ptr) { + if let Err(err) = self.cool_page(trace.record.time, page_ptr) { tracing::trace!(?page_ptr, ?err, "Unable to cool page"); } } @@ -226,7 +255,7 @@ impl sim::Classifier for HeMem { // If the page was cold and is now hot, head it if page_is_hot && !page_was_hot { tracing::trace!(?page_ptr, "Page is now hot, warming it"); - if let Err(err) = self.warm_page(page_ptr) { + if let Err(err) = self.warm_page(trace.record.time, page_ptr) { tracing::trace!(?page_ptr, ?err, "Unable to warm page"); } } @@ -265,6 +294,56 @@ impl sim::Classifier for HeMem { )?; } + { + let total_accesses = self.statistics.accesses().len(); + writeln!(f, "Total accesses: {total_accesses}")?; + + let average_prev_temperature = self + .statistics + .accesses() + .iter() + .map(|access| access.prev_temperature as f64) + .collect::(); + + let average_cur_temperature = self + .statistics + .accesses() + .iter() + .map(|access| access.cur_temperature as f64) + .collect::(); + + writeln!( + f, + "Average temperature: {:.4} ± {:.4} (Prev), {:.4} ± {:.4} (Cur)", + average_prev_temperature.mean(), + average_prev_temperature.error(), + average_cur_temperature.mean(), + average_cur_temperature.error() + )?; + + let average_page_locations = self + .statistics + .page_locations() + .iter() + .map(|(_, locations)| locations.len() as f64) + .collect::(); + let (min_page_locations, max_page_locations) = self + .statistics + .page_locations() + .iter() + .map(|(_, locations)| locations.len() as f64) + .minmax() + .into_option() + .unwrap_or((f64::NEG_INFINITY, f64::INFINITY)); + + writeln!( + f, + "Average page locations: {:.4} ± {:.4} ({min_page_locations:.2}..{max_page_locations:.2})", + average_page_locations.mean(), + average_page_locations.error() + )?; + } + Ok(()) } } diff --git a/src/classifiers/hemem/page_table.rs b/src/classifiers/hemem/page_table.rs index bd30f32..f20f152 100644 --- a/src/classifiers/hemem/page_table.rs +++ b/src/classifiers/hemem/page_table.rs @@ -68,7 +68,7 @@ impl PageTable { } /// Returns the `count` coldest pages in memory `mem_idx` - // TODO: Optimize this function? Runs in `O(N)` with all memories + // TODO: Optimize this function? Runs in `O(N)` with all pages pub fn coldest_pages(&mut self, mem_idx: MemIdx, count: usize) -> Vec { self.pages .iter_mut() diff --git a/src/classifiers/hemem/statistics.rs b/src/classifiers/hemem/statistics.rs index 3914340..249b215 100644 --- a/src/classifiers/hemem/statistics.rs +++ b/src/classifiers/hemem/statistics.rs @@ -1,25 +1,49 @@ //! Statistics // Imports -use super::{memories::MemIdx, PagePtr}; +use { + super::{memories::MemIdx, PagePtr}, + std::collections::HashMap, +}; /// Statistics #[derive(Clone, Debug)] pub struct Statistics { /// All accesses accesses: Vec, + + /// Page locations + page_locations: HashMap>, } impl Statistics { /// Creates new, empty, statistics pub fn new() -> Self { - Self { accesses: vec![] } + Self { + accesses: vec![], + page_locations: HashMap::new(), + } } /// Registers an access on these statistics pub fn register_access(&mut self, access: Access) { self.accesses.push(access); } + + /// Registers a new location for a page + pub fn register_page_location(&mut self, page_ptr: PagePtr, page_location: PageLocation) { + self.page_locations.entry(page_ptr).or_default().push(page_location); + } + + /// Returns all accesses + pub fn accesses(&self) -> &[Access] { + &self.accesses + } + + /// Returns all page locations + pub fn page_locations(&self) -> &HashMap> { + &self.page_locations + } } @@ -64,3 +88,13 @@ pub enum AccessMem { /// Page resided in memory Resided(MemIdx), } + +/// Page location +#[derive(Clone, Debug)] +pub struct PageLocation { + /// Timestamp + pub time: u64, + + /// Memory + pub mem_idx: MemIdx, +} diff --git a/src/main.rs b/src/main.rs index ccd45f0..01aad88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ fn main() -> Result<(), anyhow::Error> { tracing::trace!(target: "ftmemsim::parse_pin_trace", ?pin_trace_reader, "Parsed pin trace"); // Run the simulator - let mut sim = Simulator::new(0, Duration::from_secs_f64(0.1)); + let mut sim = Simulator::new(0, Duration::from_secs_f64(1.0)); let mut hemem = hemem::HeMem::new( hemem::Config { read_hot_threshold: 8,