mirror of
https://github.com/Zenithsiz/ftmemsim.git
synced 2026-02-15 06:57:10 +00:00
Added page locations to the HeMem statistics.
Added more details to the debug output of HeMem.
This commit is contained in:
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user