Skip to content

Commit

Permalink
tango_next_haystack()/tango_sync(seed) replaced with tango_prepare_st…
Browse files Browse the repository at this point in the history
…ate(seed)
  • Loading branch information
bazhenov committed Mar 12, 2024
1 parent c26f5aa commit a626b21
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 80 deletions.
3 changes: 2 additions & 1 deletion tango-bench/benches/tango.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ fn iqr_interquartile_range_benchmarks() -> impl IntoBenchmarks {
fn empty_benchmarks() -> impl IntoBenchmarks {
[benchmark_fn_with_setup(
"measure_empty_function",
move |_| {
move |p| {
let mut bench = benchmark_fn("_", || 42);
bench.prepare_state(p.seed);
move || bench.measure(1)
},
)]
Expand Down
31 changes: 12 additions & 19 deletions tango-bench/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,17 +338,13 @@ mod commands {
self.spi.run(self.func, iterations);
}

fn next_haystack(&mut self) {
self.spi.next_haystack(self.func);
fn prepare_state(&mut self, seed: u64) {
self.spi.prepare_state(self.func, seed);
}

fn estimate_iterations(&mut self, time_ms: u32) -> usize {
self.spi.estimate_iterations(self.func, time_ms)
}

fn sync(&mut self, seed: u64) {
self.spi.sync(self.func, seed);
}
}

/// Measure the difference in performance of two functions
Expand Down Expand Up @@ -392,19 +388,16 @@ mod commands {
let mut b_func = &mut candidate;

let seed = seed.unwrap_or_else(rand::random);
a_func.sync(seed);
b_func.sync(seed);

a_func.prepare_state(seed);
let a_estimate = (a_func.estimate_iterations(TIME_SLICE_MS) / 2).max(1);

b_func.prepare_state(seed);
let b_estimate = (b_func.estimate_iterations(TIME_SLICE_MS) / 2).max(1);

let mut iterations_per_sample = a_estimate.min(b_estimate);
let mut sampler = create_sampler(&settings, seed);

// Synchronizing test functions one more time because the estimation process may perform a different
// number of iterations on the functions, thus running them out of sync.
a_func.sync(seed);
b_func.sync(seed);

let mut i = 0;
let mut switch_counter = 0;

Expand Down Expand Up @@ -445,18 +438,18 @@ mod commands {
std::thread::yield_now();
}

let new_haystack = i % settings.samples_per_haystack == 0;
let prepare_state_seed = (i % settings.samples_per_haystack == 0).then(|| seed);
let mut sample_time = 0;

sample_time += run_func(
new_haystack,
prepare_state_seed,
a_func,
warmup_iterations,
iterations,
firewall.as_ref(),
);
sample_time += run_func(
new_haystack,
prepare_state_seed,
b_func,
warmup_iterations,
iterations,
Expand Down Expand Up @@ -504,14 +497,14 @@ mod commands {
}

fn run_func(
new_haystack: bool,
prepare_state_seed: Option<u64>,
f: &mut TestedFunction,
warmup_iterations: Option<usize>,
iterations: usize,
firewall: Option<&CacheFirewall>,
) -> u64 {
if new_haystack {
f.next_haystack();
if let Some(seed) = prepare_state_seed {
f.prepare_state(seed);
if let Some(firewall) = firewall {
firewall.issue_read();
}
Expand Down
49 changes: 12 additions & 37 deletions tango-bench/src/dylib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,9 @@ impl<'l> Spi<'l> {
self.vt.estimate_iterations(time_ms)
}

pub(crate) fn sync(&mut self, func: FunctionIdx, seed: u64) {
pub(crate) fn prepare_state(&mut self, func: FunctionIdx, seed: u64) -> bool {
self.select(func);
self.vt.sync(seed)
}

pub(crate) fn next_haystack(&mut self, func: FunctionIdx) -> bool {
self.select(func);
self.vt.next_haystack()
self.vt.prepare_state(seed)
}

fn select(&mut self, idx: usize) {
Expand Down Expand Up @@ -149,8 +144,7 @@ pub mod ffi {
type SelectFn = unsafe extern "C" fn(usize);
type RunFn = unsafe extern "C" fn(usize) -> u64;
type EstimateIterationsFn = unsafe extern "C" fn(u32) -> usize;
type NextHaystackFn = unsafe extern "C" fn() -> bool;
type SyncFn = unsafe extern "C" fn(u64);
type PrepareStateFn = unsafe extern "C" fn(u64) -> bool;
type FreeFn = unsafe extern "C" fn();

/// This block of constants is checking that all exported tango functions are of valid type according to the API.
Expand All @@ -164,7 +158,6 @@ pub mod ffi {
const TANGO_GET_TEST_NAME: GetTestNameFn = tango_get_test_name;
const TANGO_RUN: RunFn = tango_run;
const TANGO_ESTIMATE_ITERATIONS: EstimateIterationsFn = tango_estimate_iterations;
const TANGO_SYNC: SyncFn = tango_sync;
const TANGO_FREE: FreeFn = tango_free;
}

Expand Down Expand Up @@ -211,21 +204,14 @@ pub mod ffi {
}

#[no_mangle]
unsafe extern "C" fn tango_next_haystack() -> bool {
unsafe extern "C" fn tango_prepare_state(seed: u64) -> bool {
if let Some(s) = STATE.as_mut() {
s.selected_mut().next_haystack()
s.selected_mut().prepare_state(seed)
} else {
false
}
}

#[no_mangle]
unsafe extern "C" fn tango_sync(seed: u64) {
if let Some(s) = STATE.as_mut() {
s.selected_mut().sync(seed)
}
}

#[no_mangle]
unsafe extern "C" fn tango_free() {
STATE.take();
Expand All @@ -238,8 +224,7 @@ pub mod ffi {
fn get_test_name(&self, ptr: *mut *const c_char, len: *mut usize);
fn run(&self, iterations: usize) -> u64;
fn estimate_iterations(&self, time_ms: u32) -> usize;
fn next_haystack(&self) -> bool;
fn sync(&self, seed: u64);
fn prepare_state(&self, seed: u64) -> bool;
}

pub(super) static mut SELF_SPI: Option<SelfVTable> = Some(SelfVTable);
Expand Down Expand Up @@ -276,12 +261,8 @@ pub mod ffi {
unsafe { tango_estimate_iterations(time_ms) }
}

fn next_haystack(&self) -> bool {
unsafe { tango_next_haystack() }
}

fn sync(&self, seed: u64) {
unsafe { tango_sync(seed) }
fn prepare_state(&self, seed: u64) -> bool {
unsafe { tango_prepare_state(seed) }
}
}

Expand All @@ -300,8 +281,7 @@ pub mod ffi {
get_test_name_fn: Symbol<'l, GetTestNameFn>,
run_fn: Symbol<'l, RunFn>,
estimate_iterations_fn: Symbol<'l, EstimateIterationsFn>,
next_haystack_fn: Symbol<'l, NextHaystackFn>,
sync_fn: Symbol<'l, SyncFn>,
prepare_state_fn: Symbol<'l, PrepareStateFn>,
free_fn: Symbol<'l, FreeFn>,
}

Expand All @@ -315,8 +295,7 @@ pub mod ffi {
get_test_name_fn: lookup_symbol(library, "tango_get_test_name")?,
run_fn: lookup_symbol(library, "tango_run")?,
estimate_iterations_fn: lookup_symbol(library, "tango_estimate_iterations")?,
next_haystack_fn: lookup_symbol(library, "tango_next_haystack")?,
sync_fn: lookup_symbol(library, "tango_sync")?,
prepare_state_fn: lookup_symbol(library, "tango_prepare_state")?,
free_fn: lookup_symbol(library, "tango_free")?,
})
}
Expand Down Expand Up @@ -348,12 +327,8 @@ pub mod ffi {
unsafe { (self.estimate_iterations_fn)(time_ms) }
}

fn next_haystack(&self) -> bool {
unsafe { (self.next_haystack_fn)() }
}

fn sync(&self, seed: u64) {
unsafe { (self.sync_fn)(seed) }
fn prepare_state(&self, seed: u64) -> bool {
unsafe { (self.prepare_state_fn)(seed) }
}
}

Expand Down
35 changes: 12 additions & 23 deletions tango-bench/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,7 @@ pub trait MeasureTarget {
/// Calling this method should update internal haystack used for measurement. Returns `true` if update happend,
/// `false` if implementation doesn't support haystack generation.
/// Haystack/Needle distinction is described in [`Generator`] trait.
fn next_haystack(&mut self) -> bool;

/// Synchronize RNG state
///
/// If this implementation has linked generator with RNG state, this method should delegate to
/// [`Generator::sync()`]
fn sync(&mut self, seed: u64);
fn prepare_state(&mut self, seed: u64) -> bool;

/// Name of the benchmark
fn name(&self) -> &str;
Expand All @@ -177,7 +171,6 @@ pub struct BenchParams {

struct Bench<B, I, T> {
name: String,
seed: u64,
bench: B,
iter: Option<I>,
_phantom: PhantomData<T>,
Expand All @@ -197,7 +190,6 @@ pub fn benchmark_fn_with_setup<
name,
bench,
iter: None,
seed: 0,
_phantom: PhantomData,
};
Box::new(bench)
Expand All @@ -216,10 +208,10 @@ impl<T: FnMut(&BenchParams) -> I, I: BenchmarkIteration<O>, O> BenchmarkSample<I

impl<S: BenchmarkSample<I, O>, I: BenchmarkIteration<O>, O> MeasureTarget for Bench<S, I, O> {
fn measure(&mut self, iterations: usize) -> u64 {
if self.iter.is_none() {
self.next_haystack();
}
let iter = self.iter.as_mut().unwrap();
let iter = self
.iter
.as_mut()
.expect("No prepare_state() was called before measure()");

if mem::needs_drop::<O>() {
let mut result = Vec::with_capacity(iterations);
Expand All @@ -240,13 +232,11 @@ impl<S: BenchmarkSample<I, O>, I: BenchmarkIteration<O>, O> MeasureTarget for Be
}
}

fn next_haystack(&mut self) -> bool {
self.iter = Some(self.bench.create_iter(&BenchParams { seed: self.seed }));
fn prepare_state(&mut self, seed: u64) -> bool {
self.iter = Some(self.bench.create_iter(&BenchParams { seed }));
true
}

fn sync(&mut self, seed: u64) {}

fn name(&self) -> &str {
self.name.as_str()
}
Expand Down Expand Up @@ -312,18 +302,16 @@ where
}
}

fn next_haystack(&mut self) -> bool {
self.haystack = Some(self.g.borrow_mut().next_haystack());
fn prepare_state(&mut self, seed: u64) -> bool {
let generator = &mut self.g.borrow_mut();
generator.sync(seed);
self.haystack = Some(generator.next_haystack());
true
}

fn name(&self) -> &str {
&self.name
}

fn sync(&mut self, seed: u64) {
self.g.borrow_mut().sync(seed)
}
}

/// Matrix of functions is used to perform benchmark with different generator strategies.
Expand Down Expand Up @@ -1151,6 +1139,7 @@ mod tests {
let mut target = benchmark_fn("foo", move || {
thread::sleep(Duration::from_millis(expected_delay))
});
target.prepare_state(0);

let median = median_execution_time(target.as_mut(), 10).as_millis() as u64;
assert!(median < expected_delay * 10);
Expand Down

0 comments on commit a626b21

Please sign in to comment.