Skip to content

Commit ee7ec41

Browse files
authored
Merge pull request #23 from mtak-/starvation
starvation freedom - and use criterion for rbtree benchmarks
2 parents 5bede68 + 6137be0 commit ee7ec41

File tree

18 files changed

+865
-356
lines changed

18 files changed

+865
-356
lines changed

build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
macro_rules! stats {
2+
($($(#[$attr:meta])* $names:ident: $kinds:tt @ $env_var:ident),* $(,)*) => {
3+
concat!($("cargo:rerun-if-env-changed=", "SWYM_", stringify!($env_var),"\n"),*)
4+
};
5+
}
6+
7+
fn main() {
8+
println!(include!("./src/stats_list.rs"));
9+
}

ci/rbtree.sh

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -30,43 +30,11 @@ cargo check --features debug-alloc,nightly,stats,$RTM --benches --bins --example
3030
# run tests
3131
./x.py test
3232
RUST_TEST_THREADS=1 cargo test --features stats,nightly,$RTM --lib --tests
33-
RUST_TEST_THREADS=1 RUSTFLAGS="${RUSTFLAGS} ${ASAN_FLAG}" \
34-
time cargo test \
35-
--features debug-alloc,stats,$RTM
3633

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

40-
# these benchmarks are run one at a time due to high memory usage
41-
./x.py bench --features nightly,$RTM rbtree::contains_key_01
42-
./x.py bench --features nightly,$RTM rbtree::contains_key_02
43-
./x.py bench --features nightly,$RTM rbtree::contains_key_03
44-
./x.py bench --features nightly,$RTM rbtree::contains_key_04
45-
./x.py bench --features nightly,$RTM rbtree::contains_key_05
46-
./x.py bench --features nightly,$RTM rbtree::contains_key_06
47-
./x.py bench --features nightly,$RTM rbtree::contains_key_07
48-
./x.py bench --features nightly,$RTM rbtree::contains_key_08
49-
./x.py bench --features nightly,$RTM rbtree::entry_01
50-
./x.py bench --features nightly,$RTM rbtree::entry_02
51-
./x.py bench --features nightly,$RTM rbtree::entry_03
52-
./x.py bench --features nightly,$RTM rbtree::entry_04
53-
./x.py bench --features nightly,$RTM rbtree::entry_05
54-
./x.py bench --features nightly,$RTM rbtree::entry_06
55-
./x.py bench --features nightly,$RTM rbtree::entry_07
56-
./x.py bench --features nightly,$RTM rbtree::entry_08
57-
./x.py bench --features nightly,$RTM rbtree::get_01
58-
./x.py bench --features nightly,$RTM rbtree::get_02
59-
./x.py bench --features nightly,$RTM rbtree::get_03
60-
./x.py bench --features nightly,$RTM rbtree::get_04
61-
./x.py bench --features nightly,$RTM rbtree::get_05
62-
./x.py bench --features nightly,$RTM rbtree::get_06
63-
./x.py bench --features nightly,$RTM rbtree::get_07
64-
./x.py bench --features nightly,$RTM rbtree::get_08
65-
./x.py bench --features nightly,$RTM rbtree::insert_01
66-
./x.py bench --features nightly,$RTM rbtree::insert_02
67-
./x.py bench --features nightly,$RTM rbtree::insert_03
68-
./x.py bench --features nightly,$RTM rbtree::insert_04
69-
./x.py bench --features nightly,$RTM rbtree::insert_05
70-
./x.py bench --features nightly,$RTM rbtree::insert_06
71-
./x.py bench --features nightly,$RTM rbtree::insert_07
72-
./x.py bench --features nightly,$RTM rbtree::insert_08
39+
# benchmarks
40+
./x.py bench --features nightly,$RTM

src/internal.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod bloom;
1111
mod commit;
1212
mod gc;
1313
mod parking;
14+
mod starvation;
1415

1516
pub mod epoch;
1617
pub mod read_log;

src/internal/alloc/fvec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use core::ops::{Deref, DerefMut};
22

3-
const START_SIZE: usize = 1024;
3+
const START_SIZE: usize = 0;
44

55
#[derive(Debug)]
66
pub struct FVec<T> {

src/internal/commit.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const MAX_HTX_RETRIES: u8 = 3;
1818
impl<'tcell> Logs<'tcell> {
1919
#[inline]
2020
pub unsafe fn remove_writes_from_reads(&mut self) {
21-
#[allow(unused_mut)]
2221
let mut count = 0;
2322

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

108107
#[inline]
109108
unsafe fn commit_empty_write_log(self) -> bool {
110-
let (_, logs) = self.into_inner();
109+
let (_, logs, progress) = self.into_inner();
110+
progress.progressed();
111111
// RwTx validates reads as they occur. As a result, if there are no writes, then we have
112112
// no work to do in our commit algorithm.
113113
//
@@ -136,6 +136,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
136136
#[inline]
137137
fn commit_slow(self) -> bool {
138138
let mut retry_count = 0;
139+
self.progress().wait_for_starvers();
139140
match self.start_htx(&mut retry_count) {
140141
Ok(htx) => {
141142
let success = self.commit_hard(htx);
@@ -147,7 +148,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
147148
self.commit_soft()
148149
}
149150
Err(BoundedHtxErr::AbortOrConflict) => {
150-
stats::htm_conflicts(retry_count as _);
151+
stats::htm_abort(retry_count as _);
151152
false
152153
}
153154
}
@@ -156,7 +157,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
156157
#[inline(never)]
157158
fn commit_hard(self, htx: HardwareTx) -> bool {
158159
unsafe {
159-
let (synch, logs) = self.into_inner();
160+
let (synch, logs, progress) = self.into_inner();
160161
let current = synch.current_epoch();
161162
logs.read_log.validate_reads_htm(current, &htx);
162163
let park_status = logs.write_log.write_and_lock_htm(&htx, current);
@@ -168,6 +169,8 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
168169

169170
logs.read_log.clear();
170171
logs.write_log.clear_no_drop();
172+
173+
progress.progressed();
171174
if unlikely!(park_status == ParkStatus::HasParked) {
172175
crate::internal::parking::unpark();
173176
}
@@ -234,7 +237,7 @@ impl<'tx, 'tcell> PinRw<'tx, 'tcell> {
234237

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

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

252255
logs.read_log.clear();
253256
logs.write_log.clear_no_drop();
257+
progress.progressed();
254258
if unlikely!(park_status == ParkStatus::HasParked) {
255259
crate::internal::parking::unpark();
256260
}

src/internal/epoch.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const FIRST: Storage = TICK_SIZE + UNPARK_BIT;
5252
/// The smallest difference between points on the EpochClock.
5353
///
5454
/// Two is used because the first bit of EpochLock is reserved as the UNPARK_BIT
55-
const TICK_SIZE: Storage = 1 << 1;
55+
pub const TICK_SIZE: Storage = 1 << 1;
5656

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

126+
#[inline]
127+
pub fn get(self) -> NonZeroStorage {
128+
self.0
129+
}
130+
126131
/// Returns the maximum value that a QuiesceEpoch can hold. This is useful for finding the
127132
/// minimum of a set of epochs.
128133
#[inline]
@@ -487,6 +492,7 @@ impl ThreadEpoch {
487492

488493
/// A monotonically increasing clock.
489494
#[derive(Debug)]
495+
#[repr(align(64))]
490496
pub struct EpochClock(HtmStorage);
491497

492498
/// The world clock. The source of truth, and synchronization for swym. Every write transaction

src/internal/optim.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub fn _likely(b: bool) -> bool {
3434
b
3535
}
3636

37+
#[cold]
3738
pub fn _abort() -> ! {
3839
std::process::abort();
3940
}

src/internal/parking.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::{
55
},
66
stats,
77
};
8-
use crossbeam_utils::Backoff;
98
use parking_lot_core::{FilterOp, ParkResult, ParkToken, DEFAULT_UNPARK_TOKEN};
109
use swym_htm::{BoundedHtxErr, HardwareTx};
1110

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

2726
#[inline(never)]
2827
#[cold]
29-
pub fn park<'tx, 'tcell>(mut pin: PinRw<'tx, 'tcell>, backoff: &Backoff) {
28+
pub fn park<'tx, 'tcell>(mut pin: PinRw<'tx, 'tcell>) {
3029
debug_assert!(
3130
parkable(pin.reborrow()),
3231
"`AWAIT_RETRY` on a transaction that has an empty read set causes the thread to sleep \
@@ -50,12 +49,10 @@ pub fn park<'tx, 'tcell>(mut pin: PinRw<'tx, 'tcell>, backoff: &Backoff) {
5049
debug_assert_eq!(token, DEFAULT_UNPARK_TOKEN);
5150
let parked_size = logs.read_log.len() + logs.write_log.epoch_locks().count();
5251
stats::parked_size(parked_size);
53-
backoff.reset()
5452
}
5553
ParkResult::Invalid => {
5654
let parked_size = logs.read_log.len() + logs.write_log.epoch_locks().count();
5755
stats::park_failure_size(parked_size);
58-
backoff.snooze()
5956
}
6057
ParkResult::TimedOut => {
6158
if cfg!(debug_assertions) {

src/internal/read_log.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
};
1414
use swym_htm::HardwareTx;
1515

16-
const READ_CAPACITY: usize = 1024;
16+
const READ_CAPACITY: usize = 0;
1717

1818
#[derive(Debug)]
1919
pub struct ReadLog<'tcell> {

0 commit comments

Comments
 (0)