Skip to content

Commit

Permalink
Add perf event support (#315)
Browse files Browse the repository at this point in the history
* Add perfmon to Stats
* Configure perf events with MMTk options
* Include perf_counter feature in CI
  • Loading branch information
caizixian committed Jun 26, 2021
1 parent a288b40 commit 9d7305e
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 24 deletions.
1 change: 1 addition & 0 deletions .github/scripts/ci-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ if [[ $arch == "x86_64" && $os == "linux" ]]; then
cargo build --target i686-unknown-linux-gnu
for_all_features "cargo build --target i686-unknown-linux-gnu"
for_all_features "cargo build --release --target i686-unknown-linux-gnu"
cargo build --features perf_counter
fi
2 changes: 1 addition & 1 deletion .github/scripts/ci-common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ init_non_exclusive_features() {

while IFS= read -r line; do
# Only parse non mutally exclusive features
if [[ $line == *"-- Non mutally exclusive features --"* ]]; then
if [[ $line == *"-- Non mutually exclusive features --"* ]]; then
parse_features=true
continue
fi
Expand Down
3 changes: 3 additions & 0 deletions .github/scripts/ci-style.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ cargo clippy --manifest-path=vmbindings/dummyvm/Cargo.toml
if [[ $arch == "x86_64" && $os == "linux" ]]; then
for_all_features "cargo clippy --target i686-unknown-linux-gnu"
for_all_features "cargo clippy --release --target i686-unknown-linux-gnu"
cargo clippy --features perf_counter
cargo clippy --release --features perf_counter
cargo clippy --tests --features perf_counter
fi

# check format
Expand Down
1 change: 1 addition & 0 deletions .github/scripts/ci-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ for_all_features "cargo test"
# For x86_64-linux, also check for i686
if [[ $arch == "x86_64" && $os == "linux" ]]; then
for_all_features "cargo test --target i686-unknown-linux-gnu"
cargo test --features perf_counter
fi

python examples/build.py
Expand Down
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ atomic-traits = "0.2.0"
atomic = "0.4.6"
spin = "0.5.2"
env_logger = "0.8.2"
pfm = {version = "0.0.7", optional = true}

[dev-dependencies]
crossbeam = "0.7.3"
Expand All @@ -41,11 +42,15 @@ rand = "0.7.3"
[features]
default = []

# This feature is only supported on x86-64 for now
# It's manually added to CI scripts
perf_counter = ["pfm"]

# .github/scripts/ci-common.sh extracts features from the following part (including from comments).
# So be careful when editing or adding stuff to the section below.

# Do not modify the following line - ci-common.sh matches it
# -- Non mutally exclusive features --
# -- Non mutually exclusive features --

# spaces
vm_space = []
Expand Down
2 changes: 1 addition & 1 deletion src/scheduler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) use scheduler::MMTkScheduler;
pub(self) use scheduler::Scheduler;

mod stat;
mod work_counter;
pub(self) mod work_counter;

mod work;
pub use work::CoordinatorWork;
Expand Down
1 change: 1 addition & 0 deletions src/scheduler/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ mod tests {
// println!("Original: {:?}", data);

SCHEDULER.initialize(NUM_WORKERS, &(), VMThread::UNINITIALIZED);
SCHEDULER.enable_stat();
SCHEDULER.work_buckets[WorkBucketStage::Unconstrained]
.add(Sort(unsafe { &mut *(data as *mut _) }));
SCHEDULER.wait_for_completion();
Expand Down
61 changes: 52 additions & 9 deletions src/scheduler/stat.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
//! Statistics for work packets
use super::work_counter::{WorkCounter, WorkCounterBase, WorkDuration};
#[cfg(feature = "perf_counter")]
use crate::scheduler::work_counter::WorkPerfEvent;
use crate::scheduler::Context;
use crate::vm::VMBinding;
use crate::MMTK;
use std::any::TypeId;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, Ordering};

/// Merge and print the work-packet level statistics from all worker threads
Expand Down Expand Up @@ -99,7 +105,7 @@ impl SchedulerStat {
stat
}
/// Merge work counters from different worker threads
pub fn merge(&mut self, stat: &WorkerLocalStat) {
pub fn merge<C>(&mut self, stat: &WorkerLocalStat<C>) {
// 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);
Expand Down Expand Up @@ -144,7 +150,7 @@ 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) {
pub fn end_of_work<C: Context>(&self, worker_stat: &mut WorkerLocalStat<C>) {
if !worker_stat.is_enabled() {
return;
};
Expand All @@ -165,15 +171,27 @@ impl WorkStat {
}

/// Worker thread local counterpart of [`SchedulerStat`]
#[derive(Default)]
pub struct WorkerLocalStat {
pub struct WorkerLocalStat<C> {
work_id_name_map: HashMap<TypeId, &'static str>,
work_counts: HashMap<TypeId, usize>,
work_counters: HashMap<TypeId, Vec<Box<dyn WorkCounter>>>,
enabled: AtomicBool,
_phantom: PhantomData<C>,
}

impl<C> Default for WorkerLocalStat<C> {
fn default() -> Self {
WorkerLocalStat {
work_id_name_map: Default::default(),
work_counts: Default::default(),
work_counters: Default::default(),
enabled: AtomicBool::new(false),
_phantom: Default::default(),
}
}
}

impl WorkerLocalStat {
impl<C: Context> WorkerLocalStat<C> {
#[inline]
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::SeqCst)
Expand All @@ -185,23 +203,48 @@ impl WorkerLocalStat {
/// 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 {
pub fn measure_work(
&mut self,
work_id: TypeId,
work_name: &'static str,
context: &'static C,
) -> WorkStat {
let stat = WorkStat {
type_id: work_id,
type_name: work_name,
};
if self.is_enabled() {
self.work_counters
.entry(work_id)
.or_insert_with(WorkerLocalStat::counter_set)
.or_insert_with(|| C::counter_set(context))
.iter_mut()
.for_each(|c| c.start());
}
stat
}
}

// The set of work counters for all work packet types
fn counter_set() -> Vec<Box<dyn WorkCounter>> {
/// Private trait to let different contexts supply different sets of default
/// counters
trait HasCounterSet {
fn counter_set(context: &'static Self) -> Vec<Box<dyn WorkCounter>>;
}

impl<C> HasCounterSet for C {
default fn counter_set(_context: &'static Self) -> Vec<Box<dyn WorkCounter>> {
vec![Box::new(WorkDuration::new())]
}
}

/// Specialization for MMTk to read the options
#[allow(unused_variables, unused_mut)]
impl<VM: VMBinding> HasCounterSet for MMTK<VM> {
fn counter_set(mmtk: &'static Self) -> Vec<Box<dyn WorkCounter>> {
let mut counters: Vec<Box<dyn WorkCounter>> = vec![Box::new(WorkDuration::new())];
#[cfg(feature = "perf_counter")]
for e in &mmtk.options.perf_events.events {
counters.push(box WorkPerfEvent::new(&e.0, e.1, e.2));
}
counters
}
}
2 changes: 1 addition & 1 deletion src/scheduler/work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub trait Work<C: Context>: 'static + Send {
fn do_work_with_stat(&mut self, worker: &mut Worker<C>, context: &'static C) {
let stat = worker
.stat
.measure_work(TypeId::of::<Self>(), type_name::<Self>());
.measure_work(TypeId::of::<Self>(), type_name::<Self>(), context);
self.do_work(worker, context);
stat.end_of_work(&mut worker.stat);
}
Expand Down
81 changes: 81 additions & 0 deletions src/scheduler/work_counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,84 @@ impl WorkCounter for WorkDuration {
&mut self.base
}
}

#[cfg(feature = "perf_counter")]
mod perf_event {
//! Measure the perf events of work packets
//!
//! This is built on top of libpfm4.
//! The events to measure are parsed from MMTk option `perf_events`
use super::*;
use libc::{c_int, pid_t};
use pfm::PerfEvent;
use std::fmt;

/// Work counter for perf events
#[derive(Clone)]
pub struct WorkPerfEvent {
base: WorkCounterBase,
running: bool,
event_name: String,
pe: PerfEvent,
}

impl WorkPerfEvent {
/// Create a work counter
///
/// See `perf_event_open` for more details on `pid` and `cpu`
/// Examples:
/// 0, -1 measures the calling thread on all CPUs
/// -1, 0 measures all threads on CPU 0
/// -1, -1 is invalid
pub fn new(name: &str, pid: pid_t, cpu: c_int) -> WorkPerfEvent {
let mut pe = PerfEvent::new(name)
.unwrap_or_else(|_| panic!("Failed to create perf event {}", name));
pe.open(pid, cpu)
.unwrap_or_else(|_| panic!("Failed to open perf event {}", name));
WorkPerfEvent {
base: Default::default(),
running: false,
event_name: name.to_string(),
pe,
}
}
}

impl fmt::Debug for WorkPerfEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WorkPerfEvent")
.field("base", &self.base)
.field("running", &self.running)
.field("event_name", &self.event_name)
.finish()
}
}

impl WorkCounter for WorkPerfEvent {
fn start(&mut self) {
self.running = true;
self.pe.reset();
self.pe.enable();
}
fn stop(&mut self) {
self.running = true;
let perf_event_value = self.pe.read().unwrap();
self.base.merge_val(perf_event_value.value as f64);
// assert not multiplexing
assert_eq!(perf_event_value.time_enabled, perf_event_value.time_running);
self.pe.disable();
}
fn name(&self) -> String {
self.event_name.to_owned()
}
fn get_base(&self) -> &WorkCounterBase {
&self.base
}
fn get_base_mut(&mut self) -> &mut WorkCounterBase {
&mut self.base
}
}
}

#[cfg(feature = "perf_counter")]
pub(super) use perf_event::WorkPerfEvent;
2 changes: 1 addition & 1 deletion src/scheduler/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub struct Worker<C: Context> {
local: WorkerLocalPtr,
pub local_work_bucket: WorkBucket<C>,
pub sender: Sender<CoordinatorMessage<C>>,
pub stat: WorkerLocalStat,
pub stat: WorkerLocalStat<C>,
context: Option<&'static C>,
is_coordinator: bool,
local_work_buffer: Vec<(WorkBucketStage, Box<dyn Work<C>>)>,
Expand Down
Loading

0 comments on commit 9d7305e

Please sign in to comment.