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" version = "0.1.0"
dependencies = [ dependencies = [
"itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"polling_serial 0.1.0", "polling_serial 0.1.0",
"static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -88,7 +89,7 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.68" version = "0.2.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -115,7 +116,7 @@ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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 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 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 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 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 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" "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 } itertools = { version = "0.9.0", default-features = false }
nix = { version = "0.17.0", optional = true } nix = { version = "0.17.0", optional = true }
libc = { version = "0.2.69", optional = true }
[features] [features]
std = ["nix", "itertools/use_std"] std = ["nix", "itertools/use_std", "libc"]
no_std = ["polling_serial", "vga_buffer"] no_std = ["polling_serial", "vga_buffer"]
default = ["std"] 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, Debug,
} }
pub struct HistParams {
iterations: u32,
bucket_size: usize,
bucket_number: usize,
}
extern crate alloc; extern crate alloc;
use crate::calibration::Verbosity::*; use crate::calibration::Verbosity::*;
use crate::complex_addressing::AddressHasher; use crate::complex_addressing::AddressHasher;
@ -165,7 +171,7 @@ pub fn calibrate_flush(
panic!("not aligned nicely"); panic!("not aligned nicely");
} }
calibrate_impl( calibrate_impl_fixed_freq(
pointer, pointer,
cache_line_size, cache_line_size,
array.len() as isize, array.len() as isize,
@ -181,10 +187,13 @@ pub fn calibrate_flush(
display_name: "clflush miss", display_name: "clflush miss",
}, },
], ],
CFLUSH_BUCKET_NUMBER, HistParams {
CFLUSH_BUCKET_SIZE, bucket_number: CFLUSH_BUCKET_NUMBER,
CFLUSH_NUM_ITER, bucket_size: CFLUSH_BUCKET_SIZE,
iterations: CFLUSH_NUM_ITER,
},
verbose_level, verbose_level,
None,
) )
} }
@ -213,39 +222,41 @@ pub unsafe fn calibrate(
num_iterations: u32, num_iterations: u32,
verbosity_level: Verbosity, verbosity_level: Verbosity,
) -> Vec<CalibrateResult> { ) -> Vec<CalibrateResult> {
calibrate_impl( calibrate_impl_fixed_freq(
p, p,
increment, increment,
len, len,
operations, operations,
buckets_num, HistParams {
bucket_number: buckets_num,
bucket_size, bucket_size,
num_iterations, iterations: num_iterations,
},
verbosity_level, verbosity_level,
None,
) )
} }
const SPURIOUS_THRESHOLD: u32 = 1; const SPURIOUS_THRESHOLD: u32 = 1;
fn calibrate_impl( fn calibrate_impl_fixed_freq(
p: *const u8, p: *const u8,
increment: usize, increment: usize,
len: isize, len: isize,
operations: &[CalibrateOperation], operations: &[CalibrateOperation],
buckets_num: usize, hist_params: HistParams,
bucket_size: usize,
num_iterations: u32,
verbosity_level: Verbosity, verbosity_level: Verbosity,
hasher: Option<&AddressHasher>,
) -> Vec<CalibrateResult> { ) -> Vec<CalibrateResult> {
// TODO : adapt this to detect CPU generation and grab the correct masks. // TODO : adapt this to detect CPU generation and grab the correct masks.
// These are the skylake masks. // These are the skylake masks.
let masks: [usize; 3] = [ /*let masks: [usize; 3] = [
0b1111_0011_0011_0011_0010_0100_1100_0100_000000, 0b1111_0011_0011_0011_0010_0100_1100_0100_000000,
0b1011_1010_1101_0111_1110_1010_1010_0010_000000, 0b1011_1010_1101_0111_1110_1010_1010_0010_000000,
0b0110_1101_0111_1101_0101_1101_0101_0001_000000, 0b0110_1101_0111_1101_0101_1101_0101_0001_000000,
]; ];
let hasher = AddressHasher::new(&masks); let hasher = AddressHasher::new(&masks);
*/
if verbosity_level >= Thresholds { if verbosity_level >= Thresholds {
println!( println!(
"Calibrating {}...", "Calibrating {}...",
@ -256,12 +267,16 @@ fn calibrate_impl(
); );
} }
let to_bucket = |time: u64| -> usize { time as usize / bucket_size }; let to_bucket = |time: u64| -> usize { time as usize / hist_params.bucket_size };
let from_bucket = |bucket: usize| -> u64 { (bucket * bucket_size) as u64 }; let from_bucket = |bucket: usize| -> u64 { (bucket * hist_params.bucket_size) as u64 };
let mut ret = Vec::new(); let mut ret = Vec::new();
if verbosity_level >= Thresholds { if verbosity_level >= Thresholds {
print!("CSV: address, ");
if hasher.is_some() {
print!("hash, ");
}
println!( println!(
"CSV: address, hash, {} min, {} median, {} max", "{} min, {} median, {} max",
operations operations
.iter() .iter()
.map(|operation| operation.name) .map(|operation| operation.name)
@ -277,8 +292,12 @@ fn calibrate_impl(
); );
} }
if verbosity_level >= RawResult { if verbosity_level >= RawResult {
print!("RESULT:address,");
if hasher.is_some() {
print!("hash,");
}
println!( println!(
"RESULT:address,hash,time,{}", "time,{}",
operations operations
.iter() .iter()
.map(|operation| operation.name) .map(|operation| operation.name)
@ -288,10 +307,14 @@ fn calibrate_impl(
for i in (0..len).step_by(increment) { for i in (0..len).step_by(increment) {
let pointer = unsafe { p.offset(i) }; 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 { 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 // TODO add some useful impl to CalibrateResults
@ -305,10 +328,10 @@ fn calibrate_impl(
calibrate_result.histogram.reserve(operations.len()); calibrate_result.histogram.reserve(operations.len());
for op in operations { for op in operations {
let mut hist = vec![0; buckets_num]; let mut hist = vec![0; hist_params.bucket_number];
for _ in 0..num_iterations { for _ in 0..hist_params.iterations {
let time = unsafe { (op.op)(pointer) }; 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; hist[bucket] += 1;
} }
calibrate_result.histogram.push(hist); calibrate_result.histogram.push(hist);
@ -319,12 +342,16 @@ fn calibrate_impl(
let median_thresholds: Vec<u32> = calibrate_result let median_thresholds: Vec<u32> = calibrate_result
.histogram .histogram
.iter() .iter()
.map(|h| (num_iterations - h[buckets_num - 1]) / 2) .map(|h| (hist_params.iterations - h[hist_params.bucket_number - 1]) / 2)
.collect(); .collect();
for j in 0..buckets_num - 1 { for j in 0..hist_params.bucket_number - 1 {
if verbosity_level >= RawResult { 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. // ignore the last bucket : spurious context switches etc.
for op in 0..operations.len() { for op in 0..operations.len() {
@ -367,10 +394,12 @@ fn calibrate_impl(
calibrate_result.max[j] calibrate_result.max[j]
); );
} }
print!("CSV: {:p}, ", pointer);
if let Some(h) = hash {
print!("{:x}, ", h)
}
println!( println!(
"CSV: {:p}, {:x}, {}, {}, {}", "{}, {}, {}",
pointer,
hash,
calibrate_result.min.iter().format(", "), calibrate_result.min.iter().format(", "),
calibrate_result.median.iter().format(", "), calibrate_result.median.iter().format(", "),
calibrate_result.max.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 pointer = (&array[0]) as *const u8;
let r = calibrate_impl( let r = calibrate_impl_fixed_freq(
pointer, pointer,
cache_line_size, cache_line_size,
array.len() as isize, array.len() as isize,
@ -401,10 +430,13 @@ pub fn calibrate_L3_miss_hit(
name: "l3_hit", name: "l3_hit",
display_name: "L3 hit", display_name: "L3 hit",
}], }],
512, HistParams {
2, bucket_number: 512,
1 << 11, bucket_size: 2,
iterations: 1 << 11,
},
verbose_level, verbose_level,
None,
); );
r.into_iter().next().unwrap() 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 mmap;
pub mod prefetcher; pub mod prefetcher;
pub mod frequency;
use core::arch::x86_64 as arch_x86; use core::arch::x86_64 as arch_x86;
use core::ptr; use core::ptr;

View File

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

View File

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

View File

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