2020-04-13 11:16:46 +02:00
|
|
|
#![allow(clippy::missing_safety_doc)]
|
|
|
|
|
2020-07-07 15:07:55 +02:00
|
|
|
use crate::complex_addressing::cache_slicing;
|
2020-02-19 15:13:24 +01:00
|
|
|
use crate::{flush, maccess, rdtsc_fence};
|
2020-03-09 14:27:32 +01:00
|
|
|
|
2020-05-28 11:22:50 +02:00
|
|
|
use cpuid::MicroArchitecture;
|
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
use core::arch::x86_64 as arch_x86;
|
2020-03-09 14:27:32 +01:00
|
|
|
#[cfg(feature = "no_std")]
|
2020-04-01 16:31:06 +02:00
|
|
|
use polling_serial::{serial_print as print, serial_println as println};
|
2020-02-17 15:28:10 +01:00
|
|
|
|
2020-07-02 15:40:30 +02:00
|
|
|
//#[cfg(feature = "use_std")]
|
|
|
|
//use nix::errno::Errno;
|
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
use nix::sched::{sched_getaffinity, sched_setaffinity, CpuSet};
|
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
use nix::unistd::Pid;
|
|
|
|
//#[cfg(feature = "use_std")]
|
|
|
|
//use nix::Error::Sys;
|
|
|
|
#[cfg(feature = "use_std")]
|
2020-07-07 15:07:55 +02:00
|
|
|
use nix::Error;
|
|
|
|
#[cfg(feature = "use_std")]
|
2020-07-02 15:40:30 +02:00
|
|
|
use std::sync::Arc;
|
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
use std::thread;
|
|
|
|
|
2020-05-28 11:22:50 +02:00
|
|
|
extern crate alloc;
|
|
|
|
use crate::calibration::Verbosity::*;
|
|
|
|
use alloc::vec;
|
|
|
|
use alloc::vec::Vec;
|
|
|
|
use core::cmp::min;
|
2020-07-07 15:07:55 +02:00
|
|
|
use core::ptr::null_mut;
|
|
|
|
use core::sync::atomic::{spin_loop_hint, AtomicBool, AtomicPtr, Ordering};
|
2020-05-28 11:22:50 +02:00
|
|
|
use itertools::Itertools;
|
2020-07-06 09:59:01 +02:00
|
|
|
|
2020-07-02 15:40:30 +02:00
|
|
|
use atomic::Atomic;
|
|
|
|
|
2020-03-12 14:20:06 +01:00
|
|
|
#[derive(Ord, PartialOrd, Eq, PartialEq)]
|
|
|
|
pub enum Verbosity {
|
|
|
|
NoOutput,
|
|
|
|
Thresholds,
|
|
|
|
RawResult,
|
|
|
|
Debug,
|
|
|
|
}
|
|
|
|
|
2020-05-11 17:04:33 +02:00
|
|
|
pub struct HistParams {
|
2020-07-02 15:39:37 +02:00
|
|
|
pub iterations: u32,
|
|
|
|
pub bucket_size: usize,
|
|
|
|
pub bucket_number: usize,
|
2020-05-11 17:04:33 +02:00
|
|
|
}
|
|
|
|
|
2020-02-25 08:27:40 +01:00
|
|
|
pub unsafe fn only_reload(p: *const u8) -> u64 {
|
2020-02-17 15:28:10 +01:00
|
|
|
let t = rdtsc_fence();
|
|
|
|
maccess(p);
|
2020-02-21 11:36:22 +01:00
|
|
|
rdtsc_fence() - t
|
2020-02-17 15:28:10 +01:00
|
|
|
}
|
|
|
|
|
2020-02-25 08:27:40 +01:00
|
|
|
pub unsafe fn flush_and_reload(p: *const u8) -> u64 {
|
2020-02-17 15:28:10 +01:00
|
|
|
flush(p);
|
2020-07-02 15:39:37 +02:00
|
|
|
only_reload(p)
|
2020-02-17 15:28:10 +01:00
|
|
|
}
|
|
|
|
|
2020-07-07 15:07:55 +02:00
|
|
|
pub unsafe fn reload_and_flush(p: *const u8) -> u64 {
|
|
|
|
let r = only_reload(p);
|
|
|
|
flush(p);
|
|
|
|
r
|
|
|
|
}
|
|
|
|
|
2020-05-27 14:02:19 +02:00
|
|
|
pub unsafe fn only_flush(p: *const u8) -> u64 {
|
2020-02-18 08:45:15 +01:00
|
|
|
let t = rdtsc_fence();
|
|
|
|
flush(p);
|
2020-02-21 11:36:22 +01:00
|
|
|
rdtsc_fence() - t
|
2020-02-18 08:45:15 +01:00
|
|
|
}
|
|
|
|
|
2020-05-27 14:02:19 +02:00
|
|
|
pub unsafe fn load_and_flush(p: *const u8) -> u64 {
|
|
|
|
maccess(p);
|
2020-07-02 15:39:37 +02:00
|
|
|
only_flush(p)
|
2020-02-18 08:45:15 +01:00
|
|
|
}
|
2020-05-27 14:02:19 +02:00
|
|
|
|
|
|
|
pub unsafe fn flush_and_flush(p: *const u8) -> u64 {
|
|
|
|
flush(p);
|
2020-07-02 15:39:37 +02:00
|
|
|
only_flush(p)
|
2020-02-25 08:27:40 +01:00
|
|
|
}
|
2020-02-18 08:45:15 +01:00
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
pub unsafe fn l3_and_reload(p: *const u8) -> u64 {
|
|
|
|
flush(p);
|
|
|
|
arch_x86::_mm_mfence();
|
|
|
|
arch_x86::_mm_prefetch(p as *const i8, arch_x86::_MM_HINT_T2);
|
|
|
|
arch_x86::__cpuid_count(0, 0);
|
2020-07-02 15:39:37 +02:00
|
|
|
only_reload(p)
|
2020-04-01 16:12:15 +02:00
|
|
|
}
|
|
|
|
|
2020-02-18 08:45:15 +01:00
|
|
|
const BUCKET_SIZE: usize = 5;
|
|
|
|
const BUCKET_NUMBER: usize = 250;
|
|
|
|
|
2020-03-09 12:10:41 +01:00
|
|
|
// TODO same as below, also add the whole page calibration
|
|
|
|
|
2020-02-28 12:03:51 +01:00
|
|
|
pub fn calibrate_access(array: &[u8; 4096]) -> u64 {
|
2020-03-09 12:10:41 +01:00
|
|
|
println!("Calibrating...");
|
2020-02-17 15:28:10 +01:00
|
|
|
|
2020-02-18 08:45:15 +01:00
|
|
|
// Allocate a target array
|
|
|
|
// TBD why size, why the position in the array, why the type (usize)
|
2020-02-28 12:03:51 +01:00
|
|
|
// let mut array = Vec::<usize>::with_capacity(5 << 10);
|
|
|
|
// array.resize(5 << 10, 1);
|
2020-02-17 15:28:10 +01:00
|
|
|
|
2020-02-28 12:03:51 +01:00
|
|
|
// let array = array.into_boxed_slice();
|
2020-02-17 15:28:10 +01:00
|
|
|
|
2020-02-18 08:45:15 +01:00
|
|
|
// Histograms bucket of 5 and max at 400 cycles
|
|
|
|
// Magic numbers to be justified
|
|
|
|
// 80 is a size of screen
|
2020-02-21 11:36:22 +01:00
|
|
|
let mut hit_histogram = vec![0; BUCKET_NUMBER]; //Vec::<u32>::with_capacity(BUCKET_NUMBER);
|
|
|
|
//hit_histogram.resize(BUCKET_NUMBER, 0);
|
2020-02-17 15:28:10 +01:00
|
|
|
|
|
|
|
let mut miss_histogram = hit_histogram.clone();
|
|
|
|
|
2020-02-18 08:45:15 +01:00
|
|
|
// the address in memory we are going to target
|
2020-02-28 12:03:51 +01:00
|
|
|
let pointer = &array[0] as *const u8;
|
|
|
|
|
2020-03-09 12:10:41 +01:00
|
|
|
println!("buffer start {:p}", pointer);
|
2020-02-28 12:03:51 +01:00
|
|
|
|
|
|
|
if pointer as usize & 0x3f != 0 {
|
|
|
|
panic!("not aligned nicely");
|
|
|
|
}
|
2020-02-17 15:28:10 +01:00
|
|
|
|
2020-02-18 08:45:15 +01:00
|
|
|
// do a large sample of accesses to a cached line
|
2020-02-17 15:28:10 +01:00
|
|
|
unsafe { maccess(pointer) };
|
2020-02-28 12:03:51 +01:00
|
|
|
for i in 0..(4 << 10) {
|
|
|
|
for _ in 0..(1 << 10) {
|
|
|
|
let d = unsafe { only_reload(pointer.offset(i & (!0x3f))) } as usize;
|
|
|
|
hit_histogram[min(BUCKET_NUMBER - 1, d / BUCKET_SIZE) as usize] += 1;
|
|
|
|
}
|
2020-02-17 15:28:10 +01:00
|
|
|
}
|
|
|
|
|
2020-02-18 08:45:15 +01:00
|
|
|
// do a large numer of accesses to uncached line
|
2020-02-17 15:28:10 +01:00
|
|
|
unsafe { flush(pointer) };
|
2020-02-28 12:03:51 +01:00
|
|
|
for i in 0..(4 << 10) {
|
|
|
|
for _ in 0..(1 << 10) {
|
|
|
|
let d = unsafe { flush_and_reload(pointer.offset(i & (!0x3f))) } as usize;
|
|
|
|
miss_histogram[min(BUCKET_NUMBER - 1, d / BUCKET_SIZE) as usize] += 1;
|
|
|
|
}
|
2020-02-17 15:28:10 +01:00
|
|
|
}
|
|
|
|
|
2020-02-18 08:45:15 +01:00
|
|
|
let mut hit_max = 0;
|
|
|
|
let mut hit_max_i = 0;
|
|
|
|
let mut miss_min_i = 0;
|
|
|
|
for i in 0..hit_histogram.len() {
|
2020-03-09 12:10:41 +01:00
|
|
|
println!(
|
2020-02-18 08:45:15 +01:00
|
|
|
"{:3}: {:10} {:10}",
|
|
|
|
i * BUCKET_SIZE,
|
|
|
|
hit_histogram[i],
|
|
|
|
miss_histogram[i]
|
|
|
|
);
|
|
|
|
if hit_max < hit_histogram[i] {
|
|
|
|
hit_max = hit_histogram[i];
|
|
|
|
hit_max_i = i;
|
|
|
|
}
|
|
|
|
if miss_histogram[i] > 3 /* Magic */ && miss_min_i == 0 {
|
|
|
|
miss_min_i = i
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 12:10:41 +01:00
|
|
|
println!("Miss min {}", miss_min_i * BUCKET_SIZE);
|
|
|
|
println!("Max hit {}", hit_max_i * BUCKET_SIZE);
|
2020-02-18 08:45:15 +01:00
|
|
|
|
|
|
|
let mut min = u32::max_value();
|
|
|
|
let mut min_i = 0;
|
|
|
|
for i in hit_max_i..miss_min_i {
|
|
|
|
if min > hit_histogram[i] + miss_histogram[i] {
|
|
|
|
min = hit_histogram[i] + miss_histogram[i];
|
|
|
|
min_i = i;
|
|
|
|
}
|
|
|
|
}
|
2020-02-17 15:28:10 +01:00
|
|
|
|
2020-03-09 12:10:41 +01:00
|
|
|
println!("Threshold {}", min_i * BUCKET_SIZE);
|
|
|
|
println!("Calibration done.");
|
2020-02-18 08:45:15 +01:00
|
|
|
(min_i * BUCKET_SIZE) as u64
|
2020-02-17 15:28:10 +01:00
|
|
|
}
|
|
|
|
|
2020-07-02 15:39:37 +02:00
|
|
|
pub const CFLUSH_BUCKET_SIZE: usize = 1;
|
|
|
|
pub const CFLUSH_BUCKET_NUMBER: usize = 500;
|
2020-02-18 08:45:15 +01:00
|
|
|
|
2020-07-02 15:39:37 +02:00
|
|
|
pub const CFLUSH_NUM_ITER: u32 = 1 << 11;
|
2020-03-12 14:20:06 +01:00
|
|
|
|
|
|
|
pub fn calibrate_flush(
|
|
|
|
array: &[u8],
|
|
|
|
cache_line_size: usize,
|
|
|
|
verbose_level: Verbosity,
|
2020-04-01 16:12:15 +02:00
|
|
|
) -> Vec<CalibrateResult> {
|
|
|
|
let pointer = (&array[0]) as *const u8;
|
|
|
|
|
|
|
|
if pointer as usize & (cache_line_size - 1) != 0 {
|
|
|
|
panic!("not aligned nicely");
|
2020-03-20 16:09:03 +01:00
|
|
|
}
|
2020-02-18 08:45:15 +01:00
|
|
|
|
2020-05-11 17:04:33 +02:00
|
|
|
calibrate_impl_fixed_freq(
|
2020-04-01 16:12:15 +02:00
|
|
|
pointer,
|
|
|
|
cache_line_size,
|
|
|
|
array.len() as isize,
|
|
|
|
&[
|
2020-05-01 10:24:15 +02:00
|
|
|
CalibrateOperation {
|
|
|
|
op: load_and_flush,
|
|
|
|
name: "clflush_hit",
|
|
|
|
display_name: "clflush hit",
|
|
|
|
},
|
|
|
|
CalibrateOperation {
|
|
|
|
op: flush_and_flush,
|
|
|
|
name: "clflush_miss",
|
|
|
|
display_name: "clflush miss",
|
|
|
|
},
|
2020-04-01 16:12:15 +02:00
|
|
|
],
|
2020-05-11 17:04:33 +02:00
|
|
|
HistParams {
|
|
|
|
bucket_number: CFLUSH_BUCKET_NUMBER,
|
|
|
|
bucket_size: CFLUSH_BUCKET_SIZE,
|
|
|
|
iterations: CFLUSH_NUM_ITER,
|
|
|
|
},
|
2020-04-01 16:12:15 +02:00
|
|
|
verbose_level,
|
|
|
|
)
|
|
|
|
}
|
2020-02-18 08:45:15 +01:00
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct CalibrateResult {
|
|
|
|
offset: isize,
|
|
|
|
histogram: Vec<Vec<u32>>,
|
|
|
|
median: Vec<u64>,
|
|
|
|
min: Vec<u64>,
|
|
|
|
max: Vec<u64>,
|
|
|
|
}
|
2020-02-18 08:45:15 +01:00
|
|
|
|
2020-05-01 10:24:15 +02:00
|
|
|
pub struct CalibrateOperation<'a> {
|
|
|
|
pub op: unsafe fn(*const u8) -> u64,
|
|
|
|
pub name: &'a str,
|
|
|
|
pub display_name: &'a str,
|
|
|
|
}
|
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
pub unsafe fn calibrate(
|
|
|
|
p: *const u8,
|
|
|
|
increment: usize,
|
|
|
|
len: isize,
|
2020-05-01 10:24:15 +02:00
|
|
|
operations: &[CalibrateOperation],
|
2020-04-01 16:12:15 +02:00
|
|
|
buckets_num: usize,
|
|
|
|
bucket_size: usize,
|
|
|
|
num_iterations: u32,
|
|
|
|
verbosity_level: Verbosity,
|
|
|
|
) -> Vec<CalibrateResult> {
|
2020-05-11 17:04:33 +02:00
|
|
|
calibrate_impl_fixed_freq(
|
2020-04-01 16:12:15 +02:00
|
|
|
p,
|
|
|
|
increment,
|
|
|
|
len,
|
|
|
|
operations,
|
2020-05-11 17:04:33 +02:00
|
|
|
HistParams {
|
|
|
|
bucket_number: buckets_num,
|
|
|
|
bucket_size,
|
|
|
|
iterations: num_iterations,
|
|
|
|
},
|
2020-04-01 16:12:15 +02:00
|
|
|
verbosity_level,
|
|
|
|
)
|
|
|
|
}
|
2020-02-18 08:45:15 +01:00
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
const SPURIOUS_THRESHOLD: u32 = 1;
|
2020-05-11 17:04:33 +02:00
|
|
|
fn calibrate_impl_fixed_freq(
|
2020-04-01 16:12:15 +02:00
|
|
|
p: *const u8,
|
|
|
|
increment: usize,
|
|
|
|
len: isize,
|
2020-05-01 10:24:15 +02:00
|
|
|
operations: &[CalibrateOperation],
|
2020-05-11 17:04:33 +02:00
|
|
|
hist_params: HistParams,
|
2020-04-01 16:12:15 +02:00
|
|
|
verbosity_level: Verbosity,
|
|
|
|
) -> Vec<CalibrateResult> {
|
|
|
|
if verbosity_level >= Thresholds {
|
|
|
|
println!(
|
|
|
|
"Calibrating {}...",
|
2020-05-01 10:24:15 +02:00
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| { operation.display_name })
|
|
|
|
.format(", ")
|
2020-04-01 16:12:15 +02:00
|
|
|
);
|
2020-02-18 08:45:15 +01:00
|
|
|
}
|
2020-02-28 12:03:51 +01:00
|
|
|
|
2020-05-11 17:04:33 +02:00
|
|
|
let to_bucket = |time: u64| -> usize { time as usize / hist_params.bucket_size };
|
|
|
|
let from_bucket = |bucket: usize| -> u64 { (bucket * hist_params.bucket_size) as u64 };
|
2020-05-28 11:22:50 +02:00
|
|
|
|
|
|
|
let slicing = if let Some(uarch) = MicroArchitecture::get_micro_architecture() {
|
|
|
|
Some(cache_slicing(uarch, 8))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let h = if let Some(s) = slicing {
|
|
|
|
if s.can_hash() {
|
|
|
|
Some(|addr: usize| -> usize { slicing.unwrap().hash(addr).unwrap() })
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
// TODO fix the GROSS hack of using max cpu core supported
|
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
let mut ret = Vec::new();
|
|
|
|
if verbosity_level >= Thresholds {
|
2020-05-11 17:04:33 +02:00
|
|
|
print!("CSV: address, ");
|
2020-05-28 11:22:50 +02:00
|
|
|
if h.is_some() {
|
2020-05-11 17:04:33 +02:00
|
|
|
print!("hash, ");
|
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
println!(
|
2020-05-11 17:04:33 +02:00
|
|
|
"{} min, {} median, {} max",
|
2020-05-01 10:24:15 +02:00
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(" min, "),
|
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(" median, "),
|
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(" max, ")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if verbosity_level >= RawResult {
|
2020-05-11 17:04:33 +02:00
|
|
|
print!("RESULT:address,");
|
2020-05-28 11:22:50 +02:00
|
|
|
if h.is_some() {
|
2020-05-11 17:04:33 +02:00
|
|
|
print!("hash,");
|
|
|
|
}
|
2020-05-01 10:24:15 +02:00
|
|
|
println!(
|
2020-05-11 17:04:33 +02:00
|
|
|
"time,{}",
|
2020-05-01 10:24:15 +02:00
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(",")
|
2020-04-01 16:12:15 +02:00
|
|
|
);
|
|
|
|
}
|
2020-05-01 10:24:15 +02:00
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
for i in (0..len).step_by(increment) {
|
|
|
|
let pointer = unsafe { p.offset(i) };
|
2020-05-28 11:22:50 +02:00
|
|
|
let hash = h.map(|h| h(pointer as usize));
|
2020-04-01 16:12:15 +02:00
|
|
|
|
|
|
|
if verbosity_level >= Thresholds {
|
2020-05-11 17:04:33 +02:00
|
|
|
print!("Calibration for {:p}", pointer);
|
|
|
|
if let Some(h) = hash {
|
|
|
|
print!(" (hash: {:x})", h)
|
|
|
|
}
|
|
|
|
println!();
|
2020-03-12 14:20:06 +01:00
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
|
|
|
|
// TODO add some useful impl to CalibrateResults
|
|
|
|
let mut calibrate_result = CalibrateResult {
|
|
|
|
offset: i,
|
|
|
|
histogram: Vec::new(),
|
|
|
|
median: vec![0; operations.len()],
|
|
|
|
min: vec![0; operations.len()],
|
|
|
|
max: vec![0; operations.len()],
|
|
|
|
};
|
|
|
|
calibrate_result.histogram.reserve(operations.len());
|
|
|
|
|
|
|
|
for op in operations {
|
2020-05-11 17:04:33 +02:00
|
|
|
let mut hist = vec![0; hist_params.bucket_number];
|
|
|
|
for _ in 0..hist_params.iterations {
|
2020-05-01 10:24:15 +02:00
|
|
|
let time = unsafe { (op.op)(pointer) };
|
2020-05-11 17:04:33 +02:00
|
|
|
let bucket = min(hist_params.bucket_number - 1, to_bucket(time));
|
2020-04-01 16:12:15 +02:00
|
|
|
hist[bucket] += 1;
|
|
|
|
}
|
|
|
|
calibrate_result.histogram.push(hist);
|
2020-02-28 12:03:51 +01:00
|
|
|
}
|
2020-02-18 08:45:15 +01:00
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
let mut sums = vec![0; operations.len()];
|
|
|
|
|
|
|
|
let median_thresholds: Vec<u32> = calibrate_result
|
|
|
|
.histogram
|
|
|
|
.iter()
|
2020-05-11 17:04:33 +02:00
|
|
|
.map(|h| (hist_params.iterations - h[hist_params.bucket_number - 1]) / 2)
|
2020-04-01 16:12:15 +02:00
|
|
|
.collect();
|
2020-02-18 17:07:59 +01:00
|
|
|
|
2020-05-11 17:04:33 +02:00
|
|
|
for j in 0..hist_params.bucket_number - 1 {
|
2020-04-01 16:12:15 +02:00
|
|
|
if verbosity_level >= RawResult {
|
2020-05-11 17:04:33 +02:00
|
|
|
print!("RESULT:{:p},", pointer);
|
|
|
|
if let Some(h) = hash {
|
|
|
|
print!("{:x},", h);
|
|
|
|
}
|
|
|
|
print!("{}", from_bucket(j));
|
2020-02-28 12:03:51 +01:00
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
// ignore the last bucket : spurious context switches etc.
|
|
|
|
for op in 0..operations.len() {
|
|
|
|
let hist = &calibrate_result.histogram[op][j];
|
|
|
|
let min = &mut calibrate_result.min[op];
|
|
|
|
let max = &mut calibrate_result.max[op];
|
|
|
|
let med = &mut calibrate_result.median[op];
|
|
|
|
let sum = &mut sums[op];
|
|
|
|
if verbosity_level >= RawResult {
|
2020-05-01 10:24:15 +02:00
|
|
|
print!(",{}", hist);
|
2020-04-01 16:12:15 +02:00
|
|
|
}
|
2020-03-12 14:20:06 +01:00
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
if *min == 0 {
|
2020-03-12 14:20:06 +01:00
|
|
|
// looking for min
|
2020-04-01 16:12:15 +02:00
|
|
|
if *hist > SPURIOUS_THRESHOLD {
|
|
|
|
*min = from_bucket(j);
|
2020-03-12 14:20:06 +01:00
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
} else if *hist > SPURIOUS_THRESHOLD {
|
|
|
|
*max = from_bucket(j);
|
2020-03-12 14:20:06 +01:00
|
|
|
}
|
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
if *med == 0 {
|
|
|
|
*sum += *hist;
|
|
|
|
if *sum >= median_thresholds[op] {
|
|
|
|
*med = from_bucket(j);
|
2020-03-12 14:20:06 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-28 12:03:51 +01:00
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
if verbosity_level >= RawResult {
|
|
|
|
println!();
|
2020-03-12 14:20:06 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
if verbosity_level >= Thresholds {
|
2020-05-01 10:24:15 +02:00
|
|
|
for (j, op) in operations.iter().enumerate() {
|
2020-04-01 16:12:15 +02:00
|
|
|
println!(
|
|
|
|
"{}: min {}, median {}, max {}",
|
2020-05-01 10:24:15 +02:00
|
|
|
op.display_name,
|
2020-04-01 16:12:15 +02:00
|
|
|
calibrate_result.min[j],
|
|
|
|
calibrate_result.median[j],
|
|
|
|
calibrate_result.max[j]
|
|
|
|
);
|
2020-02-28 12:03:51 +01:00
|
|
|
}
|
2020-05-11 17:04:33 +02:00
|
|
|
print!("CSV: {:p}, ", pointer);
|
|
|
|
if let Some(h) = hash {
|
|
|
|
print!("{:x}, ", h)
|
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
println!(
|
2020-05-11 17:04:33 +02:00
|
|
|
"{}, {}, {}",
|
2020-04-01 16:12:15 +02:00
|
|
|
calibrate_result.min.iter().format(", "),
|
|
|
|
calibrate_result.median.iter().format(", "),
|
|
|
|
calibrate_result.max.iter().format(", ")
|
|
|
|
);
|
2020-03-20 16:09:03 +01:00
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
ret.push(calibrate_result);
|
2020-02-28 12:03:51 +01:00
|
|
|
}
|
2020-03-12 14:20:06 +01:00
|
|
|
ret
|
2020-02-17 15:28:10 +01:00
|
|
|
}
|
2020-04-01 16:12:15 +02:00
|
|
|
|
2020-07-02 15:40:30 +02:00
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
pub struct CalibrateOperation2T<'a> {
|
|
|
|
pub prepare: unsafe fn(*const u8) -> (),
|
|
|
|
pub op: unsafe fn(*const u8) -> u64,
|
|
|
|
pub name: &'a str,
|
|
|
|
pub display_name: &'a str,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
pub struct CalibrateResult2T {
|
|
|
|
pub main_core: usize,
|
|
|
|
pub helper_core: usize,
|
|
|
|
pub res: Result<Vec<CalibrateResult>, nix::Error>, // TODO
|
|
|
|
|
2020-07-07 15:07:55 +02:00
|
|
|
// TODO
|
2020-07-02 15:40:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn wait(turn_lock: &AtomicBool, turn: bool) {
|
|
|
|
while turn_lock.load(Ordering::Acquire) != turn {
|
|
|
|
spin_loop_hint();
|
|
|
|
}
|
|
|
|
assert_eq!(turn_lock.load(Ordering::Relaxed), turn);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn next(turn_lock: &AtomicBool) {
|
|
|
|
turn_lock.fetch_xor(true, Ordering::Release);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
pub unsafe fn calibrate_fixed_freq_2_thread<I: Iterator<Item = (usize, usize)>>(
|
|
|
|
p: *const u8,
|
|
|
|
increment: usize,
|
|
|
|
len: isize,
|
|
|
|
cores: &mut I,
|
|
|
|
operations: &[CalibrateOperation2T],
|
|
|
|
hist_params: HistParams,
|
|
|
|
verbosity_level: Verbosity,
|
|
|
|
) -> Vec<CalibrateResult2T> {
|
2020-07-07 15:07:55 +02:00
|
|
|
calibrate_fixed_freq_2_thread_impl(
|
|
|
|
p,
|
|
|
|
increment,
|
|
|
|
len,
|
|
|
|
cores,
|
|
|
|
operations,
|
|
|
|
hist_params,
|
|
|
|
verbosity_level,
|
|
|
|
)
|
2020-07-02 15:40:30 +02:00
|
|
|
}
|
2020-07-06 09:59:01 +02:00
|
|
|
|
|
|
|
#[cfg(feature = "use_std")]
|
2020-07-02 15:40:30 +02:00
|
|
|
fn calibrate_fixed_freq_2_thread_impl<I: Iterator<Item = (usize, usize)>>(
|
|
|
|
p: *const u8,
|
|
|
|
increment: usize,
|
|
|
|
len: isize,
|
|
|
|
cores: &mut I,
|
|
|
|
operations: &[CalibrateOperation2T],
|
|
|
|
hist_params: HistParams,
|
|
|
|
verbosity_level: Verbosity,
|
|
|
|
) -> Vec<CalibrateResult2T> {
|
|
|
|
if verbosity_level >= Thresholds {
|
|
|
|
println!(
|
|
|
|
"Calibrating {}...",
|
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| { operation.display_name })
|
|
|
|
.format(", ")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let to_bucket = |time: u64| -> usize { time as usize / hist_params.bucket_size };
|
|
|
|
let from_bucket = |bucket: usize| -> u64 { (bucket * hist_params.bucket_size) as u64 };
|
|
|
|
|
|
|
|
let slicing = if let Some(uarch) = MicroArchitecture::get_micro_architecture() {
|
|
|
|
Some(cache_slicing(uarch, 8))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let h = if let Some(s) = slicing {
|
|
|
|
if s.can_hash() {
|
|
|
|
Some(|addr: usize| -> usize { slicing.unwrap().hash(addr).unwrap() })
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut ret = Vec::new();
|
|
|
|
|
2020-07-07 15:07:55 +02:00
|
|
|
let helper_thread_params = Arc::new(HelperThreadParams {
|
2020-07-02 15:40:30 +02:00
|
|
|
turn: AtomicBool::new(false),
|
|
|
|
stop: AtomicBool::new(true),
|
|
|
|
op: Atomic::new(operations[0].prepare),
|
|
|
|
address: AtomicPtr::new(null_mut()),
|
|
|
|
});
|
|
|
|
|
|
|
|
if verbosity_level >= Thresholds {
|
|
|
|
print!("CSV: main_core, helper_core, address, ");
|
|
|
|
if h.is_some() {
|
|
|
|
print!("hash, ");
|
|
|
|
}
|
|
|
|
println!(
|
|
|
|
"{} min, {} median, {} max",
|
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(" min, "),
|
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(" median, "),
|
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(" max, ")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if verbosity_level >= RawResult {
|
|
|
|
print!("RESULT:main_core,helper_core,address,");
|
|
|
|
if h.is_some() {
|
|
|
|
print!("hash,");
|
|
|
|
}
|
|
|
|
println!(
|
|
|
|
"time,{}",
|
|
|
|
operations
|
|
|
|
.iter()
|
|
|
|
.map(|operation| operation.name)
|
|
|
|
.format(",")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let old = sched_getaffinity(Pid::from_raw(0)).unwrap();
|
|
|
|
|
|
|
|
for (main_core, helper_core) in cores {
|
|
|
|
// set main thread affinity
|
|
|
|
|
|
|
|
if verbosity_level >= Thresholds {
|
2020-07-07 15:07:55 +02:00
|
|
|
println!(
|
|
|
|
"Calibration for main_core {}, helper {}.",
|
|
|
|
main_core, helper_core
|
|
|
|
);
|
2020-07-02 15:40:30 +02:00
|
|
|
}
|
|
|
|
|
2020-07-07 15:07:55 +02:00
|
|
|
eprintln!(
|
|
|
|
"Calibration for main_core {}, helper {}.",
|
|
|
|
main_core, helper_core
|
|
|
|
);
|
2020-07-03 10:27:49 +02:00
|
|
|
|
2020-07-02 15:40:30 +02:00
|
|
|
let mut core = CpuSet::new();
|
|
|
|
match core.set(main_core) {
|
2020-07-07 15:07:55 +02:00
|
|
|
Ok(_) => {}
|
2020-07-02 15:40:30 +02:00
|
|
|
Err(e) => {
|
2020-07-07 15:07:55 +02:00
|
|
|
ret.push(CalibrateResult2T {
|
|
|
|
main_core,
|
|
|
|
helper_core,
|
|
|
|
res: Err(e),
|
|
|
|
});
|
2020-07-02 15:40:30 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match sched_setaffinity(Pid::from_raw(0), &core) {
|
2020-07-07 15:07:55 +02:00
|
|
|
Ok(_) => {}
|
2020-07-02 15:40:30 +02:00
|
|
|
Err(e) => {
|
2020-07-07 15:07:55 +02:00
|
|
|
ret.push(CalibrateResult2T {
|
|
|
|
main_core,
|
|
|
|
helper_core,
|
|
|
|
res: Err(e),
|
|
|
|
});
|
2020-07-02 15:40:30 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
helper_thread_params.stop.store(false, Ordering::Relaxed);
|
|
|
|
// set up the helper thread
|
|
|
|
|
|
|
|
let htp = helper_thread_params.clone();
|
|
|
|
let hc = helper_core;
|
2020-07-07 15:07:55 +02:00
|
|
|
let helper_thread = thread::spawn(move || calibrate_fixed_freq_2_thread_helper(htp, hc));
|
2020-07-02 15:40:30 +02:00
|
|
|
|
|
|
|
// do the calibration
|
|
|
|
let mut calibrate_result_vec = Vec::new();
|
|
|
|
|
|
|
|
for i in (0..len).step_by(increment) {
|
|
|
|
let pointer = unsafe { p.offset(i) };
|
2020-07-07 15:07:55 +02:00
|
|
|
helper_thread_params
|
|
|
|
.address
|
|
|
|
.store(pointer as *mut u8, Ordering::Relaxed);
|
2020-07-02 15:40:30 +02:00
|
|
|
|
|
|
|
let hash = h.map(|h| h(pointer as usize));
|
|
|
|
|
|
|
|
if verbosity_level >= Thresholds {
|
|
|
|
print!("Calibration for {:p}", pointer);
|
|
|
|
if let Some(h) = hash {
|
|
|
|
print!(" (hash: {:x})", h)
|
|
|
|
}
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO add some useful impl to CalibrateResults
|
|
|
|
let mut calibrate_result = CalibrateResult {
|
|
|
|
offset: i,
|
|
|
|
histogram: Vec::new(),
|
|
|
|
median: vec![0; operations.len()],
|
|
|
|
min: vec![0; operations.len()],
|
|
|
|
max: vec![0; operations.len()],
|
|
|
|
};
|
|
|
|
calibrate_result.histogram.reserve(operations.len());
|
|
|
|
|
|
|
|
for op in operations {
|
|
|
|
helper_thread_params.op.store(op.prepare, Ordering::Relaxed);
|
|
|
|
let mut hist = vec![0; hist_params.bucket_number];
|
2020-07-03 10:27:49 +02:00
|
|
|
for _ in 0..hist_params.iterations {
|
|
|
|
next(&helper_thread_params.turn);
|
|
|
|
wait(&helper_thread_params.turn, false);
|
|
|
|
let _time = unsafe { (op.op)(pointer) };
|
|
|
|
}
|
2020-07-02 15:40:30 +02:00
|
|
|
for _ in 0..hist_params.iterations {
|
|
|
|
next(&helper_thread_params.turn);
|
|
|
|
wait(&helper_thread_params.turn, false);
|
|
|
|
let time = unsafe { (op.op)(pointer) };
|
|
|
|
let bucket = min(hist_params.bucket_number - 1, to_bucket(time));
|
|
|
|
hist[bucket] += 1;
|
|
|
|
}
|
|
|
|
calibrate_result.histogram.push(hist);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut sums = vec![0; operations.len()];
|
|
|
|
|
|
|
|
let median_thresholds: Vec<u32> = calibrate_result
|
|
|
|
.histogram
|
|
|
|
.iter()
|
|
|
|
.map(|h| (hist_params.iterations - h[hist_params.bucket_number - 1]) / 2)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
for j in 0..hist_params.bucket_number - 1 {
|
|
|
|
if verbosity_level >= RawResult {
|
|
|
|
print!("RESULT:{},{},{:p},", main_core, helper_core, pointer);
|
|
|
|
if let Some(h) = hash {
|
|
|
|
print!("{:x},", h);
|
|
|
|
}
|
|
|
|
print!("{}", from_bucket(j));
|
|
|
|
}
|
|
|
|
// ignore the last bucket : spurious context switches etc.
|
|
|
|
for op in 0..operations.len() {
|
|
|
|
let hist = &calibrate_result.histogram[op][j];
|
|
|
|
let min = &mut calibrate_result.min[op];
|
|
|
|
let max = &mut calibrate_result.max[op];
|
|
|
|
let med = &mut calibrate_result.median[op];
|
|
|
|
let sum = &mut sums[op];
|
|
|
|
if verbosity_level >= RawResult {
|
|
|
|
print!(",{}", hist);
|
|
|
|
}
|
|
|
|
|
|
|
|
if *min == 0 {
|
|
|
|
// looking for min
|
|
|
|
if *hist > SPURIOUS_THRESHOLD {
|
|
|
|
*min = from_bucket(j);
|
|
|
|
}
|
|
|
|
} else if *hist > SPURIOUS_THRESHOLD {
|
|
|
|
*max = from_bucket(j);
|
|
|
|
}
|
|
|
|
|
|
|
|
if *med == 0 {
|
|
|
|
*sum += *hist;
|
|
|
|
if *sum >= median_thresholds[op] {
|
|
|
|
*med = from_bucket(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if verbosity_level >= RawResult {
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if verbosity_level >= Thresholds {
|
|
|
|
for (j, op) in operations.iter().enumerate() {
|
|
|
|
println!(
|
|
|
|
"{}: min {}, median {}, max {}",
|
|
|
|
op.display_name,
|
|
|
|
calibrate_result.min[j],
|
|
|
|
calibrate_result.median[j],
|
|
|
|
calibrate_result.max[j]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
print!("CSV: {},{},{:p}, ", main_core, helper_core, pointer);
|
|
|
|
if let Some(h) = hash {
|
|
|
|
print!("{:x}, ", h)
|
|
|
|
}
|
|
|
|
println!(
|
|
|
|
"{}, {}, {}",
|
|
|
|
calibrate_result.min.iter().format(", "),
|
|
|
|
calibrate_result.median.iter().format(", "),
|
|
|
|
calibrate_result.max.iter().format(", ")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
calibrate_result_vec.push(calibrate_result);
|
|
|
|
}
|
|
|
|
|
2020-07-07 15:07:55 +02:00
|
|
|
ret.push(CalibrateResult2T {
|
2020-07-02 15:40:30 +02:00
|
|
|
main_core,
|
|
|
|
helper_core,
|
2020-07-07 15:07:55 +02:00
|
|
|
res: Ok(calibrate_result_vec),
|
2020-07-02 15:40:30 +02:00
|
|
|
});
|
|
|
|
// terminate the thread
|
|
|
|
helper_thread_params.stop.store(true, Ordering::Relaxed);
|
|
|
|
next(&helper_thread_params.turn);
|
|
|
|
wait(&helper_thread_params.turn, false);
|
|
|
|
// join thread.
|
|
|
|
helper_thread.join();
|
|
|
|
}
|
|
|
|
|
|
|
|
sched_setaffinity(Pid::from_raw(0), &old).unwrap();
|
|
|
|
|
|
|
|
ret
|
|
|
|
// return the result
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
struct HelperThreadParams {
|
|
|
|
turn: AtomicBool,
|
|
|
|
stop: AtomicBool,
|
|
|
|
op: Atomic<unsafe fn(*const u8)>,
|
|
|
|
address: AtomicPtr<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "use_std")]
|
|
|
|
fn calibrate_fixed_freq_2_thread_helper(
|
|
|
|
params: Arc<HelperThreadParams>,
|
|
|
|
helper_core: usize,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
// set thread affinity
|
|
|
|
let mut core = CpuSet::new();
|
|
|
|
match core.set(helper_core) {
|
2020-07-07 15:07:55 +02:00
|
|
|
Ok(_) => {}
|
2020-07-02 15:40:30 +02:00
|
|
|
Err(_e) => {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match sched_setaffinity(Pid::from_raw(0), &core) {
|
2020-07-07 15:07:55 +02:00
|
|
|
Ok(_) => {}
|
2020-07-02 15:40:30 +02:00
|
|
|
Err(_e) => {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
loop {
|
|
|
|
// grab lock
|
|
|
|
wait(¶ms.turn, true);
|
|
|
|
if params.stop.load(Ordering::Relaxed) {
|
|
|
|
next(¶ms.turn);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
// get the relevant parameters
|
|
|
|
let addr: *const u8 = params.address.load(Ordering::Relaxed);
|
|
|
|
let op = params.op.load(Ordering::Relaxed);
|
2020-07-07 15:07:55 +02:00
|
|
|
unsafe { op(addr) };
|
2020-07-02 15:40:30 +02:00
|
|
|
// release lock
|
|
|
|
next(¶ms.turn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-01 16:12:15 +02:00
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub fn calibrate_L3_miss_hit(
|
|
|
|
array: &[u8],
|
|
|
|
cache_line_size: usize,
|
|
|
|
verbose_level: Verbosity,
|
|
|
|
) -> CalibrateResult {
|
|
|
|
if verbose_level > NoOutput {
|
|
|
|
println!("Calibrating L3 access...");
|
|
|
|
}
|
|
|
|
let pointer = (&array[0]) as *const u8;
|
|
|
|
|
2020-05-11 17:04:33 +02:00
|
|
|
let r = calibrate_impl_fixed_freq(
|
2020-04-01 16:12:15 +02:00
|
|
|
pointer,
|
|
|
|
cache_line_size,
|
|
|
|
array.len() as isize,
|
2020-05-01 10:24:15 +02:00
|
|
|
&[CalibrateOperation {
|
|
|
|
op: l3_and_reload,
|
|
|
|
name: "l3_hit",
|
|
|
|
display_name: "L3 hit",
|
|
|
|
}],
|
2020-05-11 17:04:33 +02:00
|
|
|
HistParams {
|
|
|
|
bucket_number: 512,
|
|
|
|
bucket_size: 2,
|
|
|
|
iterations: 1 << 11,
|
|
|
|
},
|
2020-04-01 16:12:15 +02:00
|
|
|
verbose_level,
|
|
|
|
);
|
|
|
|
|
|
|
|
r.into_iter().next().unwrap()
|
|
|
|
}
|