Skip to content

Commit

Permalink
Merge pull request #23 from mtak-/starvation
Browse files Browse the repository at this point in the history
starvation freedom - and use criterion for rbtree benchmarks
  • Loading branch information
mtak- authored Jun 24, 2019
2 parents 5bede68 + 6137be0 commit ee7ec41
Show file tree
Hide file tree
Showing 18 changed files with 865 additions and 356 deletions.
9 changes: 9 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
macro_rules! stats {
($($(#[$attr:meta])* $names:ident: $kinds:tt @ $env_var:ident),* $(,)*) => {
concat!($("cargo:rerun-if-env-changed=", "SWYM_", stringify!($env_var),"\n"),*)
};
}

fn main() {
println!(include!("./src/stats_list.rs"));
}
44 changes: 6 additions & 38 deletions ci/rbtree.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,43 +30,11 @@ cargo check --features debug-alloc,nightly,stats,$RTM --benches --bins --example
# run tests
./x.py test
RUST_TEST_THREADS=1 cargo test --features stats,nightly,$RTM --lib --tests
RUST_TEST_THREADS=1 RUSTFLAGS="${RUSTFLAGS} ${ASAN_FLAG}" \
time cargo test \
--features debug-alloc,stats,$RTM

# benchmarks
./x.py bench insert:: --features nightly,$RTM
# TODO: address sanitizer doesn't work with criterion?
# RUSTFLAGS="${RUSTFLAGS} ${ASAN_FLAG}"
RUST_TEST_THREADS=1 \
time cargo test --features debug-alloc,stats,$RTM

# these benchmarks are run one at a time due to high memory usage
./x.py bench --features nightly,$RTM rbtree::contains_key_01
./x.py bench --features nightly,$RTM rbtree::contains_key_02
./x.py bench --features nightly,$RTM rbtree::contains_key_03
./x.py bench --features nightly,$RTM rbtree::contains_key_04
./x.py bench --features nightly,$RTM rbtree::contains_key_05
./x.py bench --features nightly,$RTM rbtree::contains_key_06
./x.py bench --features nightly,$RTM rbtree::contains_key_07
./x.py bench --features nightly,$RTM rbtree::contains_key_08
./x.py bench --features nightly,$RTM rbtree::entry_01
./x.py bench --features nightly,$RTM rbtree::entry_02
./x.py bench --features nightly,$RTM rbtree::entry_03
./x.py bench --features nightly,$RTM rbtree::entry_04
./x.py bench --features nightly,$RTM rbtree::entry_05
./x.py bench --features nightly,$RTM rbtree::entry_06
./x.py bench --features nightly,$RTM rbtree::entry_07
./x.py bench --features nightly,$RTM rbtree::entry_08
./x.py bench --features nightly,$RTM rbtree::get_01
./x.py bench --features nightly,$RTM rbtree::get_02
./x.py bench --features nightly,$RTM rbtree::get_03
./x.py bench --features nightly,$RTM rbtree::get_04
./x.py bench --features nightly,$RTM rbtree::get_05
./x.py bench --features nightly,$RTM rbtree::get_06
./x.py bench --features nightly,$RTM rbtree::get_07
./x.py bench --features nightly,$RTM rbtree::get_08
./x.py bench --features nightly,$RTM rbtree::insert_01
./x.py bench --features nightly,$RTM rbtree::insert_02
./x.py bench --features nightly,$RTM rbtree::insert_03
./x.py bench --features nightly,$RTM rbtree::insert_04
./x.py bench --features nightly,$RTM rbtree::insert_05
./x.py bench --features nightly,$RTM rbtree::insert_06
./x.py bench --features nightly,$RTM rbtree::insert_07
./x.py bench --features nightly,$RTM rbtree::insert_08
# benchmarks
./x.py bench --features nightly,$RTM
1 change: 1 addition & 0 deletions src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod bloom;
mod commit;
mod gc;
mod parking;
mod starvation;

pub mod epoch;
pub mod read_log;
Expand Down
2 changes: 1 addition & 1 deletion src/internal/alloc/fvec.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::ops::{Deref, DerefMut};

const START_SIZE: usize = 1024;
const START_SIZE: usize = 0;

#[derive(Debug)]
pub struct FVec<T> {
Expand Down
14 changes: 9 additions & 5 deletions src/internal/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const MAX_HTX_RETRIES: u8 = 3;
impl<'tcell> Logs<'tcell> {
#[inline]
pub unsafe fn remove_writes_from_reads(&mut self) {
#[allow(unused_mut)]
let mut count = 0;

let write_log = &mut self.write_log;
Expand Down Expand Up @@ -107,7 +106,8 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {

#[inline]
unsafe fn commit_empty_write_log(self) -> bool {
let (_, logs) = self.into_inner();
let (_, logs, progress) = self.into_inner();
progress.progressed();
// RwTx validates reads as they occur. As a result, if there are no writes, then we have
// no work to do in our commit algorithm.
//
Expand Down Expand Up @@ -136,6 +136,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
#[inline]
fn commit_slow(self) -> bool {
let mut retry_count = 0;
self.progress().wait_for_starvers();
match self.start_htx(&mut retry_count) {
Ok(htx) => {
let success = self.commit_hard(htx);
Expand All @@ -147,7 +148,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
self.commit_soft()
}
Err(BoundedHtxErr::AbortOrConflict) => {
stats::htm_conflicts(retry_count as _);
stats::htm_abort(retry_count as _);
false
}
}
Expand All @@ -156,7 +157,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
#[inline(never)]
fn commit_hard(self, htx: HardwareTx) -> bool {
unsafe {
let (synch, logs) = self.into_inner();
let (synch, logs, progress) = self.into_inner();
let current = synch.current_epoch();
logs.read_log.validate_reads_htm(current, &htx);
let park_status = logs.write_log.write_and_lock_htm(&htx, current);
Expand All @@ -168,6 +169,8 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {

logs.read_log.clear();
logs.write_log.clear_no_drop();

progress.progressed();
if unlikely!(park_status == ParkStatus::HasParked) {
crate::internal::parking::unpark();
}
Expand Down Expand Up @@ -234,7 +237,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {

#[inline]
unsafe fn validation_success(self, park_status: ParkStatus) -> bool {
let (synch, logs) = self.into_inner();
let (synch, logs, progress) = self.into_inner();

// The writes must be performed before the EPOCH_CLOCK is tick'ed.
// Reads can get away with performing less work with this ordering.
Expand All @@ -251,6 +254,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {

logs.read_log.clear();
logs.write_log.clear_no_drop();
progress.progressed();
if unlikely!(park_status == ParkStatus::HasParked) {
crate::internal::parking::unpark();
}
Expand Down
8 changes: 7 additions & 1 deletion src/internal/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const FIRST: Storage = TICK_SIZE + UNPARK_BIT;
/// The smallest difference between points on the EpochClock.
///
/// Two is used because the first bit of EpochLock is reserved as the UNPARK_BIT
const TICK_SIZE: Storage = 1 << 1;
pub const TICK_SIZE: Storage = 1 << 1;

/// The least significant bit is set when _no_ threads are parked waiting for modifications to an
/// EpochLock.
Expand Down Expand Up @@ -123,6 +123,11 @@ impl QuiesceEpoch {
NonZeroStorage::new(epoch).map(QuiesceEpoch)
}

#[inline]
pub fn get(self) -> NonZeroStorage {
self.0
}

/// Returns the maximum value that a QuiesceEpoch can hold. This is useful for finding the
/// minimum of a set of epochs.
#[inline]
Expand Down Expand Up @@ -487,6 +492,7 @@ impl ThreadEpoch {

/// A monotonically increasing clock.
#[derive(Debug)]
#[repr(align(64))]
pub struct EpochClock(HtmStorage);

/// The world clock. The source of truth, and synchronization for swym. Every write transaction
Expand Down
1 change: 1 addition & 0 deletions src/internal/optim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub fn _likely(b: bool) -> bool {
b
}

#[cold]
pub fn _abort() -> ! {
std::process::abort();
}
Expand Down
5 changes: 1 addition & 4 deletions src/internal/parking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::{
},
stats,
};
use crossbeam_utils::Backoff;
use parking_lot_core::{FilterOp, ParkResult, ParkToken, DEFAULT_UNPARK_TOKEN};
use swym_htm::{BoundedHtxErr, HardwareTx};

Expand All @@ -26,7 +25,7 @@ fn parkable<'tx, 'tcell>(pin: PinMutRef<'tx, 'tcell>) -> bool {

#[inline(never)]
#[cold]
pub fn park<'tx, 'tcell>(mut pin: PinRw<'tx, 'tcell>, backoff: &Backoff) {
pub fn park<'tx, 'tcell>(mut pin: PinRw<'tx, 'tcell>) {
debug_assert!(
parkable(pin.reborrow()),
"`AWAIT_RETRY` on a transaction that has an empty read set causes the thread to sleep \
Expand All @@ -50,12 +49,10 @@ pub fn park<'tx, 'tcell>(mut pin: PinRw<'tx, 'tcell>, backoff: &Backoff) {
debug_assert_eq!(token, DEFAULT_UNPARK_TOKEN);
let parked_size = logs.read_log.len() + logs.write_log.epoch_locks().count();
stats::parked_size(parked_size);
backoff.reset()
}
ParkResult::Invalid => {
let parked_size = logs.read_log.len() + logs.write_log.epoch_locks().count();
stats::park_failure_size(parked_size);
backoff.snooze()
}
ParkResult::TimedOut => {
if cfg!(debug_assertions) {
Expand Down
2 changes: 1 addition & 1 deletion src/internal/read_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
};
use swym_htm::HardwareTx;

const READ_CAPACITY: usize = 1024;
const READ_CAPACITY: usize = 0;

#[derive(Debug)]
pub struct ReadLog<'tcell> {
Expand Down
Loading

0 comments on commit ee7ec41

Please sign in to comment.