Skip to content

Commit

Permalink
More documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
caizixian committed May 20, 2021
1 parent fc44c4b commit 63e257d
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 9 deletions.
43 changes: 41 additions & 2 deletions src/scheduler/stat.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
//! Statistics for work packets
use super::work_counter::{WorkCounter, WorkCounterBase, WorkDuration};
use std::any::TypeId;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};

/// Merge and print the work-packet level statistics from all worker threads
#[derive(Default)]
pub struct SchedulerStat {
/// Map work packet type IDs to work packet names
work_id_name_map: HashMap<TypeId, &'static str>,
/// Count the number of work packets executed for different types
work_counts: HashMap<TypeId, usize>,
/// Collect work counters from work threads.
/// Two dimensional vectors are used.
/// The first dimension is for different types of work counters.
/// The second dimension if for work counters of the same type but from
/// different threads.
/// We assume different threads have the same set of work counters
/// (in the same order).
work_counters: HashMap<TypeId, Vec<Vec<Box<dyn WorkCounter>>>>,
}

Expand All @@ -22,6 +33,7 @@ impl SchedulerStat {
}
}

/// Used during statistics printing at [`crate::memory_manager::harness_end`]
pub fn harness_stat(&self) -> HashMap<String, String> {
let mut stat = HashMap::new();
// Work counts
Expand All @@ -38,13 +50,18 @@ impl SchedulerStat {
// Work execution times
let mut duration_overall: WorkCounterBase = Default::default();
for (t, vs) in &self.work_counters {
// Name of the work packet type
let n = self.work_id_name_map[t];
// Iterate through different types of work counters
for v in vs.iter() {
// Aggregate work counters of the same type but from different
// worker threads
let fold = v
.iter()
.fold(Default::default(), |acc: WorkCounterBase, x| {
acc.merge(x.get_base())
});
// Update the overall execution time
duration_overall.merge_inplace(&fold);
let name = v.first().unwrap().name();
stat.insert(
Expand All @@ -61,7 +78,7 @@ impl SchedulerStat {
);
}
}

// Print out overall execution time
stat.insert(
"total-work.time.total".to_owned(),
format!("{:.2}", duration_overall.total),
Expand All @@ -77,45 +94,63 @@ impl SchedulerStat {

stat
}

/// Merge work counters from different worker threads
pub fn merge(&mut self, stat: &WorkerLocalStat) {
// Merge work packet type ID to work packet name mapping
for (id, name) in &stat.work_id_name_map {
self.work_id_name_map.insert(*id, *name);
}
// Merge work count for different work packet types
for (id, count) in &stat.work_counts {
if self.work_counts.contains_key(id) {
*self.work_counts.get_mut(id).unwrap() += *count;
} else {
self.work_counts.insert(*id, *count);
}
}
// Merge work counter for different work packet types
for (id, counters) in &stat.work_counters {
// Initialize the two dimensional vector
// [
// [], // foo counter
// [], // bar counter
// ]
let vs = self
.work_counters
.entry(*id)
.or_insert_with(|| vec![vec![]; counters.len()]);
// [
// [counters[0] of type foo],
// [counters[1] of type bar]
// ]
for (v, c) in vs.iter_mut().zip(counters.iter()) {
v.push(c.clone());
}
}
}
}

/// Describing a single work packet
pub struct WorkStat {
type_id: TypeId,
type_name: &'static str,
}

impl WorkStat {
/// Stop all work counters for the work packet type of the just executed
/// work packet
#[inline(always)]
pub fn end_of_work(&self, worker_stat: &mut WorkerLocalStat) {
if !worker_stat.is_enabled() {
return;
};
// Insert type ID, name pair
worker_stat
.work_id_name_map
.insert(self.type_id, self.type_name);
// Increment work count
*worker_stat.work_counts.entry(self.type_id).or_insert(0) += 1;
// Stop counters
worker_stat
.work_counters
.entry(self.type_id)
Expand All @@ -125,6 +160,7 @@ impl WorkStat {
}
}

/// Worker thread local counterpart of [`SchedulerStat`]
#[derive(Default)]
pub struct WorkerLocalStat {
work_id_name_map: HashMap<TypeId, &'static str>,
Expand All @@ -142,6 +178,8 @@ impl WorkerLocalStat {
pub fn enable(&self) {
self.enabled.store(true, Ordering::SeqCst);
}
/// Measure the execution of a work packet by starting all counters for that
/// type
#[inline]
pub fn measure_work(&mut self, work_id: TypeId, work_name: &'static str) -> WorkStat {
let stat = WorkStat {
Expand All @@ -158,6 +196,7 @@ impl WorkerLocalStat {
stat
}

// The set of work counters for all work packet types
fn counter_set() -> Vec<Box<dyn WorkCounter>> {
vec![Box::new(WorkDuration::new())]
}
Expand Down
19 changes: 12 additions & 7 deletions src/scheduler/work_counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Provides an abstraction and implementations of counters for collecting
//! work-packet level statistics
//!
//! See [crate::util::statistics] for collecting statistics over a GC cycle
//! See [`crate::util::statistics`] for collecting statistics over a GC cycle
use std::time::SystemTime;

/// Common struct for different work counters
Expand All @@ -16,7 +16,7 @@ pub(super) struct WorkCounterBase {
pub(super) max: f64,
}

/// Make WorkCounter trait objects cloneable
/// Make [`WorkCounter`] trait objects cloneable
pub(super) trait WorkCounterClone {
/// Clone the object
fn clone_box(&self) -> Box<dyn WorkCounter>;
Expand All @@ -29,6 +29,11 @@ impl<T: 'static + WorkCounter + Clone> WorkCounterClone for T {
}

/// An abstraction of work counters
///
/// Use for trait objects, as we have might have types of work counters for
/// the same work packet and the types are not statically known.alloc
/// The overhead should be negligible compared with the cost of executing
/// a work packet.
pub(super) trait WorkCounter: WorkCounterClone + std::fmt::Debug {
// TODO: consolidate with crate::util::statistics::counter::Counter;
/// Start the counter
Expand All @@ -37,9 +42,9 @@ pub(super) trait WorkCounter: WorkCounterClone + std::fmt::Debug {
fn stop(&mut self);
/// Name of counter
fn name(&self) -> String;
/// Return a reference to [WorkCounterBase]
/// Return a reference to [`WorkCounterBase`]
fn get_base(&self) -> &WorkCounterBase;
/// Return a mutatable reference to [WorkCounterBase]
/// Return a mutatable reference to [`WorkCounterBase`]
fn get_base_mut(&mut self) -> &mut WorkCounterBase;
}

Expand All @@ -60,7 +65,7 @@ impl Default for WorkCounterBase {
}

impl WorkCounterBase {
/// Merge two [WorkCounterBase], keep the semantics of the fields,
/// Merge two [`WorkCounterBase`], keep the semantics of the fields,
/// and return a new object
pub(super) fn merge(&self, other: &Self) -> Self {
let min = self.min.min(other.min);
Expand All @@ -69,7 +74,7 @@ impl WorkCounterBase {
WorkCounterBase { total, min, max }
}

/// Merge two [WorkCounterBase], modify the current object in place,
/// Merge two [`WorkCounterBase`], modify the current object in place,
/// and keep the semantics of the fields
pub(super) fn merge_inplace(&mut self, other: &Self) {
self.min = self.min.min(other.min);
Expand All @@ -87,7 +92,7 @@ impl WorkCounterBase {

/// Measure the durations of work packets
///
/// Timing is based on [SystemTime]
/// Timing is based on [`SystemTime`]
#[derive(Copy, Clone, Debug)]
pub(super) struct WorkDuration {
base: WorkCounterBase,
Expand Down

0 comments on commit 63e257d

Please sign in to comment.