Added page locations to the HeMem statistics.

Added more details to the debug output of HeMem.
This commit is contained in:
2023-05-17 23:21:04 +01:00
parent 94e31534bf
commit 5b2e9addfe
7 changed files with 186 additions and 22 deletions

View File

@@ -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<MemIdx, anyhow::Error> {
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::<average::Variance>();
let average_cur_temperature = self
.statistics
.accesses()
.iter()
.map(|access| access.cur_temperature as f64)
.collect::<average::Variance>();
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::<average::Variance>();
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(())
}
}

View File

@@ -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<PagePtr> {
self.pages
.iter_mut()

View File

@@ -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<Access>,
/// Page locations
page_locations: HashMap<PagePtr, Vec<PageLocation>>,
}
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<PagePtr, Vec<PageLocation>> {
&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,
}

View File

@@ -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,