Skip to content

Commit

Permalink
Use two Cell<u16> in CounterMarker
Browse files Browse the repository at this point in the history
This improves performance when weak pointers are enabled.
  • Loading branch information
frengor committed Aug 6, 2024
1 parent b74fc58 commit 57f736f
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/cc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ impl<T: ?Sized + Trace> Cc<T> {
/// Returns the number of [`Cc`]s to the pointed allocation.
#[inline]
pub fn strong_count(&self) -> u32 {
self.counter_marker().counter()
self.counter_marker().counter() as u32
}

/// Returns `true` if the strong reference count is `1`, `false` otherwise.
Expand Down
89 changes: 44 additions & 45 deletions src/counter_marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,44 @@ use core::cell::Cell;

use crate::utils;

const NON_MARKED: u32 = 0u32;
const IN_POSSIBLE_CYCLES: u32 = 1u32 << (u32::BITS - 2);
const IN_LIST: u32 = 2u32 << (u32::BITS - 2);
const IN_QUEUE: u32 = 3u32 << (u32::BITS - 2);
const NON_MARKED: u16 = 0u16;
const IN_POSSIBLE_CYCLES: u16 = 1u16 << (u16::BITS - 2);
const IN_LIST: u16 = 2u16 << (u16::BITS - 2);
const IN_QUEUE: u16 = 3u16 << (u16::BITS - 2);

const COUNTER_MASK: u32 = 0b11111111111111u32; // First 14 bits set to 1
const TRACING_COUNTER_MASK: u32 = COUNTER_MASK << 14; // 14 bits set to 1 followed by 14 bits set to 0
const FINALIZED_MASK: u32 = 1u32 << (u32::BITS - 4);
const METADATA_MASK: u32 = 1u32 << (u32::BITS - 3);
const BITS_MASK: u32 = !(COUNTER_MASK | TRACING_COUNTER_MASK | FINALIZED_MASK | METADATA_MASK);
const FIRST_BIT_MASK: u32 = 1u32 << (u32::BITS - 1);
const COUNTER_MASK: u16 = 0b11111111111111u16; // First 14 bits set to 1
const FIRST_BIT_MASK: u16 = 1u16 << (u16::BITS - 1);
const FINALIZED_MASK: u16 = 1u16 << (u16::BITS - 2);
const BITS_MASK: u16 = !COUNTER_MASK;

const INITIAL_VALUE: u32 = COUNTER_MASK + 2; // +2 means that tracing counter and counter are both set to 1
const INITIAL_VALUE_FINALIZED: u32 = INITIAL_VALUE | FINALIZED_MASK;
const INITIAL_VALUE: u16 = 1u16;
const INITIAL_VALUE_TRACING_COUNTER: u16 = INITIAL_VALUE | NON_MARKED;
const INITIAL_VALUE_FINALIZED: u16 = INITIAL_VALUE | FINALIZED_MASK;

// pub(crate) to make it available in tests
pub(crate) const MAX: u32 = COUNTER_MASK - 1;
pub(crate) const MAX: u16 = COUNTER_MASK - 1;

/// Internal representation:
/// ```text
/// +-----------+----------+----------+------------+------------+
/// | A: 2 bits | B: 1 bit | C: 1 bit | D: 14 bits | E: 14 bits | Total: 32 bits
/// +-----------+----------+----------+------------+------------+
/// +-----------+------------+ +----------+----------+------------+
/// | A: 2 bits | B: 14 bits | | C: 1 bit | D: 1 bit | E: 14 bits | Total: 32 bits (16 + 16)
/// +-----------+------------+ +----------+----------+------------+
/// ```
///
/// * `A` has 4 possible states:
/// * `NON_MARKED`
/// * `IN_POSSIBLE_CYCLES`: in `possible_cycles` list (implies `NON_MARKED`)
/// * `IN_LIST`: in `root_list` or `non_root_list`
/// * `IN_QUEUE`: in queue to be traced
/// * `B` is `1` when metadata has been allocated, `0` otherwise
/// * `C` is `1` when the element inside `CcBox` has already been finalized, `0` otherwise
/// * `D` is the tracing counter. The max value (the one with every bit set to 1) is reserved
/// * `B` is the tracing counter. The max value (the one with every bit set to 1) is reserved
/// and indicates that the allocated value has already been dropped (but not yet deallocated)
/// * `E` is the reference counter (last one for sum/subtraction efficiency). The max value (the
/// one with every bit set to 1) is reserved and should not be used
/// * `C` is `1` when metadata has been allocated, `0` otherwise
/// * `D` is `1` when the element inside `CcBox` has already been finalized, `0` otherwise
/// * `E` is the reference counter. The max value (the one with every bit set to 1) is reserved and should not be used
#[derive(Clone, Debug)]
#[repr(transparent)]
pub(crate) struct CounterMarker {
counter: Cell<u32>,
tracing_counter: Cell<u16>,
counter: Cell<u16>,
}

pub(crate) struct OverflowError;
Expand All @@ -51,6 +49,7 @@ impl CounterMarker {
#[must_use]
pub(crate) fn new_with_counter_to_one(already_finalized: bool) -> CounterMarker {
CounterMarker {
tracing_counter: Cell::new(INITIAL_VALUE_TRACING_COUNTER),
counter: Cell::new(if !already_finalized {
INITIAL_VALUE
} else {
Expand Down Expand Up @@ -94,7 +93,7 @@ impl CounterMarker {
Err(OverflowError)
} else {
// Increment trace_counter and not counter
self.counter.set(self.counter.get() + (1u32 << 14));
self.tracing_counter.set(self.tracing_counter.get() + 1);
Ok(())
}
}
Expand All @@ -108,114 +107,114 @@ impl CounterMarker {
Err(OverflowError)
} else {
// Decrement trace_counter and not counter
self.counter.set(self.counter.get() - (1u32 << 14));
self.tracing_counter.set(self.tracing_counter.get() - 1);
Ok(())
}
}

#[inline]
pub(crate) fn counter(&self) -> u32 {
pub(crate) fn counter(&self) -> u16 {
let rc = self.counter.get() & COUNTER_MASK;
debug_assert!(rc != COUNTER_MASK); // Check for reserved value
rc
}

#[inline]
pub(crate) fn tracing_counter(&self) -> u32 {
let tc = (self.counter.get() & TRACING_COUNTER_MASK) >> 14;
pub(crate) fn tracing_counter(&self) -> u16 {
let tc = self.tracing_counter.get() & COUNTER_MASK;
debug_assert!(tc != COUNTER_MASK); // Check for reserved value
tc
}

#[inline]
pub(crate) fn reset_tracing_counter(&self) {
debug_assert!(self.tracing_counter() != COUNTER_MASK); // Check for reserved value
self.counter.set(self.counter.get() & !TRACING_COUNTER_MASK);
self.tracing_counter.set(self.tracing_counter.get() & !COUNTER_MASK);
}

#[cfg(feature = "finalization")]
#[inline]
pub(crate) fn needs_finalization(&self) -> bool {
(self.counter.get() & FINALIZED_MASK) == 0u32
(self.counter.get() & FINALIZED_MASK) == 0u16
}

#[cfg(feature = "finalization")]
#[inline]
pub(crate) fn set_finalized(&self, finalized: bool) {
self.set_bits(finalized, FINALIZED_MASK);
Self::set_bits(&self.counter, finalized, FINALIZED_MASK);
}

#[cfg(feature = "weak-ptrs")]
#[inline]
pub(crate) fn has_allocated_for_metadata(&self) -> bool {
(self.counter.get() & METADATA_MASK) == METADATA_MASK
(self.counter.get() & FIRST_BIT_MASK) == FIRST_BIT_MASK
}

#[cfg(feature = "weak-ptrs")]
#[inline]
pub(crate) fn set_allocated_for_metadata(&self, allocated_for_metadata: bool) {
self.set_bits(allocated_for_metadata, METADATA_MASK);
Self::set_bits(&self.counter, allocated_for_metadata, FIRST_BIT_MASK);
}

#[cfg(feature = "weak-ptrs")]
#[inline]
pub(crate) fn is_dropped(&self) -> bool {
(self.counter.get() & TRACING_COUNTER_MASK) == TRACING_COUNTER_MASK
(self.tracing_counter.get() & COUNTER_MASK) == COUNTER_MASK
}

#[cfg(feature = "weak-ptrs")]
#[inline]
pub(crate) fn set_dropped(&self, dropped: bool) {
self.set_bits(dropped, TRACING_COUNTER_MASK);
Self::set_bits(&self.tracing_counter, dropped, COUNTER_MASK);
}

#[inline]
pub(crate) fn is_not_marked(&self) -> bool {
// true if (self.counter & BITS_MASK) is equal to 01 or 00,
// so if the first bit is 0
(self.counter.get() & FIRST_BIT_MASK) == 0u32
(self.tracing_counter.get() & FIRST_BIT_MASK) == 0u16
}

#[inline]
pub(crate) fn is_in_possible_cycles(&self) -> bool {
(self.counter.get() & BITS_MASK) == IN_POSSIBLE_CYCLES
(self.tracing_counter.get() & BITS_MASK) == IN_POSSIBLE_CYCLES
}

#[inline]
pub(crate) fn is_in_list(&self) -> bool {
(self.counter.get() & BITS_MASK) == IN_LIST
(self.tracing_counter.get() & BITS_MASK) == IN_LIST
}

#[inline]
pub(crate) fn _is_in_queue(&self) -> bool {
(self.counter.get() & BITS_MASK) == IN_QUEUE
(self.tracing_counter.get() & BITS_MASK) == IN_QUEUE
}

#[inline]
pub(crate) fn is_in_list_or_queue(&self) -> bool {
// true if (self.counter & BITS_MASK) is equal to 10 or 11,
// so if the first bit is 1
(self.counter.get() & FIRST_BIT_MASK) == FIRST_BIT_MASK
(self.tracing_counter.get() & FIRST_BIT_MASK) == FIRST_BIT_MASK
}

#[inline]
pub(crate) fn mark(&self, new_mark: Mark) {
self.counter.set((self.counter.get() & !BITS_MASK) | (new_mark as u32));
self.tracing_counter.set((self.tracing_counter.get() & !BITS_MASK) | (new_mark as u16));
}

#[cfg(any(feature = "weak-ptrs", feature = "finalization"))]
#[inline(always)]
fn set_bits(&self, value: bool, mask: u32) {
fn set_bits(cell: &Cell<u16>, value: bool, mask: u16) {
if value {
self.counter.set(self.counter.get() | mask);
cell.set(cell.get() | mask);
} else {
self.counter.set(self.counter.get() & !mask);
cell.set(cell.get() & !mask);
}
}
}

#[derive(Copy, Clone, Debug)]
#[repr(u32)]
#[repr(u16)]
pub(crate) enum Mark {
NonMarked = NON_MARKED,
PossibleCycles = IN_POSSIBLE_CYCLES,
Expand Down
2 changes: 1 addition & 1 deletion src/weak/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl<T: ?Sized + Trace> Weak<T> {
) {
0
} else {
counter
counter as u32
}
} else {
0
Expand Down

0 comments on commit 57f736f

Please sign in to comment.