Refactor to start integrating frequency measurement and portability

- added preliminary tests of frequency measurements
- refactored the signature of calibrate function to parametrize hash function
- various cleanup
This commit is contained in:
GuillaumeDIDIER 2020-05-11 17:04:33 +02:00
parent b7e8aff723
commit fb926dfe2a
9 changed files with 210 additions and 46 deletions

7
Cargo.lock generated
View File

@ -33,6 +33,7 @@ name = "cache_utils"
version = "0.1.0"
dependencies = [
"itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"polling_serial 0.1.0",
"static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -88,7 +89,7 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.68"
version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -115,7 +116,7 @@ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -190,7 +191,7 @@ dependencies = [
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
"checksum linked_list_allocator 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5825aea823c659d0fdcdbe8c9b78baf56f3a10365d783db874f6d360df72626f"
"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
"checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"

View File

@ -14,10 +14,10 @@ static_assertions = "1.1.0"
itertools = { version = "0.9.0", default-features = false }
nix = { version = "0.17.0", optional = true }
libc = { version = "0.2.69", optional = true }
[features]
std = ["nix", "itertools/use_std"]
std = ["nix", "itertools/use_std", "libc"]
no_std = ["polling_serial", "vga_buffer"]
default = ["std"]

View File

@ -0,0 +1,64 @@
use cache_utils::frequency::get_freq_cpufreq_kernel;
use cache_utils::rdtsc_fence;
use libc::sched_getcpu;
use nix::sched::{sched_setaffinity, CpuSet};
use nix::unistd::Pid;
use static_assertions::_core::time::Duration;
use std::thread::sleep;
use std::time::Instant;
const NUM_SAMPLE: i32 = 1000000;
const NUM_SAMPLE_SLEEP: i32 = 10000;
pub fn main() {
let mut core = CpuSet::new();
core.set(unsafe { sched_getcpu() } as usize).unwrap();
sched_setaffinity(Pid::from_raw(0), &core);
let t0_pre = unsafe { rdtsc_fence() };
let start = Instant::now();
let t0_post = unsafe { rdtsc_fence() };
let mut tsc = t0_post;
println!("TSC,Freq");
for i in 0..NUM_SAMPLE {
//let t1 = unsafe { rdtsc_fence() };
let frequency = get_freq_cpufreq_kernel();
let t2 = unsafe { rdtsc_fence() };
let delta = t2 - tsc;
tsc = t2;
if let Ok(freq) = frequency {
println!("{},{}", delta, freq);
}
}
println!("Idling");
for i in 0..NUM_SAMPLE_SLEEP {
sleep(Duration::from_micros(1000));
let frequency = get_freq_cpufreq_kernel();
let t2 = unsafe { rdtsc_fence() };
let delta = t2 - tsc;
tsc = t2;
if let Ok(freq) = frequency {
println!("{},{}", delta, freq);
}
}
let tf_pre = unsafe { rdtsc_fence() };
let elapsed = start.elapsed();
let tf_post = unsafe { rdtsc_fence() };
println!(
"Time elapsed: {} us, number of tsc tick: {} - {} - {}",
elapsed.as_micros(),
tf_pre - t0_pre,
(tf_pre - t0_pre + tf_post - t0_post) / 2,
tf_post - t0_post
);
eprintln!(
"Time elapsed: {} us, number of tsc tick: {} - {} - {}",
elapsed.as_micros(),
tf_pre - t0_pre,
(tf_pre - t0_pre + tf_post - t0_post) / 2,
tf_post - t0_post
);
}

View File

@ -14,6 +14,12 @@ pub enum Verbosity {
Debug,
}
pub struct HistParams {
iterations: u32,
bucket_size: usize,
bucket_number: usize,
}
extern crate alloc;
use crate::calibration::Verbosity::*;
use crate::complex_addressing::AddressHasher;
@ -165,7 +171,7 @@ pub fn calibrate_flush(
panic!("not aligned nicely");
}
calibrate_impl(
calibrate_impl_fixed_freq(
pointer,
cache_line_size,
array.len() as isize,
@ -181,10 +187,13 @@ pub fn calibrate_flush(
display_name: "clflush miss",
},
],
CFLUSH_BUCKET_NUMBER,
CFLUSH_BUCKET_SIZE,
CFLUSH_NUM_ITER,
HistParams {
bucket_number: CFLUSH_BUCKET_NUMBER,
bucket_size: CFLUSH_BUCKET_SIZE,
iterations: CFLUSH_NUM_ITER,
},
verbose_level,
None,
)
}
@ -213,39 +222,41 @@ pub unsafe fn calibrate(
num_iterations: u32,
verbosity_level: Verbosity,
) -> Vec<CalibrateResult> {
calibrate_impl(
calibrate_impl_fixed_freq(
p,
increment,
len,
operations,
buckets_num,
bucket_size,
num_iterations,
HistParams {
bucket_number: buckets_num,
bucket_size,
iterations: num_iterations,
},
verbosity_level,
None,
)
}
const SPURIOUS_THRESHOLD: u32 = 1;
fn calibrate_impl(
fn calibrate_impl_fixed_freq(
p: *const u8,
increment: usize,
len: isize,
operations: &[CalibrateOperation],
buckets_num: usize,
bucket_size: usize,
num_iterations: u32,
hist_params: HistParams,
verbosity_level: Verbosity,
hasher: Option<&AddressHasher>,
) -> Vec<CalibrateResult> {
// TODO : adapt this to detect CPU generation and grab the correct masks.
// These are the skylake masks.
let masks: [usize; 3] = [
0b1111_0011_0011_0011_0010_0100_1100_0100_000000,
0b1011_1010_1101_0111_1110_1010_1010_0010_000000,
0b0110_1101_0111_1101_0101_1101_0101_0001_000000,
];
let hasher = AddressHasher::new(&masks);
/*let masks: [usize; 3] = [
0b1111_0011_0011_0011_0010_0100_1100_0100_000000,
0b1011_1010_1101_0111_1110_1010_1010_0010_000000,
0b0110_1101_0111_1101_0101_1101_0101_0001_000000,
];
let hasher = AddressHasher::new(&masks);
*/
if verbosity_level >= Thresholds {
println!(
"Calibrating {}...",
@ -256,12 +267,16 @@ fn calibrate_impl(
);
}
let to_bucket = |time: u64| -> usize { time as usize / bucket_size };
let from_bucket = |bucket: usize| -> u64 { (bucket * bucket_size) as u64 };
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 mut ret = Vec::new();
if verbosity_level >= Thresholds {
print!("CSV: address, ");
if hasher.is_some() {
print!("hash, ");
}
println!(
"CSV: address, hash, {} min, {} median, {} max",
"{} min, {} median, {} max",
operations
.iter()
.map(|operation| operation.name)
@ -277,8 +292,12 @@ fn calibrate_impl(
);
}
if verbosity_level >= RawResult {
print!("RESULT:address,");
if hasher.is_some() {
print!("hash,");
}
println!(
"RESULT:address,hash,time,{}",
"time,{}",
operations
.iter()
.map(|operation| operation.name)
@ -288,10 +307,14 @@ fn calibrate_impl(
for i in (0..len).step_by(increment) {
let pointer = unsafe { p.offset(i) };
let hash = hasher.hash(pointer as usize);
let hash = hasher.map(|h| h.hash(pointer as usize));
if verbosity_level >= Thresholds {
println!("Calibration for {:p} (hash: {:x})", pointer, hash);
print!("Calibration for {:p}", pointer);
if let Some(h) = hash {
print!(" (hash: {:x})", h)
}
println!();
}
// TODO add some useful impl to CalibrateResults
@ -305,10 +328,10 @@ fn calibrate_impl(
calibrate_result.histogram.reserve(operations.len());
for op in operations {
let mut hist = vec![0; buckets_num];
for _ in 0..num_iterations {
let mut hist = vec![0; hist_params.bucket_number];
for _ in 0..hist_params.iterations {
let time = unsafe { (op.op)(pointer) };
let bucket = min(buckets_num - 1, to_bucket(time));
let bucket = min(hist_params.bucket_number - 1, to_bucket(time));
hist[bucket] += 1;
}
calibrate_result.histogram.push(hist);
@ -319,12 +342,16 @@ fn calibrate_impl(
let median_thresholds: Vec<u32> = calibrate_result
.histogram
.iter()
.map(|h| (num_iterations - h[buckets_num - 1]) / 2)
.map(|h| (hist_params.iterations - h[hist_params.bucket_number - 1]) / 2)
.collect();
for j in 0..buckets_num - 1 {
for j in 0..hist_params.bucket_number - 1 {
if verbosity_level >= RawResult {
print!("RESULT:{:p},{:x},{}", pointer, hash, from_bucket(j));
print!("RESULT:{:p},", 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() {
@ -367,10 +394,12 @@ fn calibrate_impl(
calibrate_result.max[j]
);
}
print!("CSV: {:p}, ", pointer);
if let Some(h) = hash {
print!("{:x}, ", h)
}
println!(
"CSV: {:p}, {:x}, {}, {}, {}",
pointer,
hash,
"{}, {}, {}",
calibrate_result.min.iter().format(", "),
calibrate_result.median.iter().format(", "),
calibrate_result.max.iter().format(", ")
@ -392,7 +421,7 @@ pub fn calibrate_L3_miss_hit(
}
let pointer = (&array[0]) as *const u8;
let r = calibrate_impl(
let r = calibrate_impl_fixed_freq(
pointer,
cache_line_size,
array.len() as isize,
@ -401,10 +430,13 @@ pub fn calibrate_L3_miss_hit(
name: "l3_hit",
display_name: "L3 hit",
}],
512,
2,
1 << 11,
HistParams {
bucket_number: 512,
bucket_size: 2,
iterations: 1 << 11,
},
verbose_level,
None,
);
r.into_iter().next().unwrap()

View File

@ -0,0 +1,67 @@
use crate::frequency::Error::{Unimplemented, UnsupportedPlatform};
use crate::rdtsc_fence;
#[cfg(all(target_os = "linux", feature = "std"))]
use libc::sched_getcpu;
#[cfg(all(target_os = "linux", feature = "std"))]
use std::convert::TryInto;
#[cfg(all(target_os = "linux", feature = "std"))]
use std::os::raw::{c_uint, c_ulong};
pub enum Error {
InsufficentPrivileges,
UnsupportedPlatform,
Unimplemented,
}
#[cfg(all(target_os = "linux", feature = "std"))]
#[link(name = "cpupower")]
extern "C" {
//unsigned long cpufreq_get_freq_kernel(unsigned int cpu);
fn cpufreq_get_freq_kernel(cpu: c_uint) -> c_ulong;
}
pub fn get_freq_cpufreq_kernel() -> Result<u64, Error> {
// TODO Add memorization
return match unsafe { sched_getcpu() }.try_into() {
Ok(cpu) => Ok(unsafe { cpufreq_get_freq_kernel(cpu) }),
Err(e) => Err(Unimplemented),
};
}
pub fn get_frequency() -> Result<u64, Error> {
if cfg!(target_os = "linux") && cfg!(feature = "std") {
return get_freq_cpufreq_kernel();
}
if cfg!(target_os = "none") {
// TODO check CPL
// if sufficient privileges use rdmsr
// Otherwise return insufficent privileges
return Err(Unimplemented);
}
Err(UnsupportedPlatform)
}
pub fn get_frequency_change_period(period: u64) -> Result<u64, Error> {
let mut t: u64 = 0;
let mut freq: u64 = 0;
let mut last_freq_change: u64 = 0;
for _ in 0..period {
let f = get_frequency();
let time = unsafe { rdtsc_fence() };
match f {
Ok(f) => {
if f != freq {
t += time - last_freq_change;
last_freq_change = time;
freq = f;
}
}
Err(e) => {
return Err(e);
}
}
}
return Ok(t / period);
}

View File

@ -19,6 +19,8 @@ pub mod complex_addressing;
pub mod mmap;
pub mod prefetcher;
pub mod frequency;
use core::arch::x86_64 as arch_x86;
use core::ptr;

View File

@ -8,7 +8,6 @@ use core::panic::PanicInfo;
use polling_serial::{serial_print, serial_println};
use vga_buffer::println;
use bootloader::{entry_point, BootInfo};
entry_point!(test_kernel_main);

View File

@ -27,5 +27,4 @@ fn should_fail() {
fn panic(_info: &PanicInfo) -> ! {
serial_println!("[ok]");
exit_qemu(QemuExitCode::Success);
}

View File

@ -16,7 +16,7 @@
use core::panic::PanicInfo;
use lazy_static::lazy_static;
use polling_serial::{serial_print, serial_println};
use vga_buffer::{println};
use vga_buffer::println;
use volatile::Volatile;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};