Skip to content

Commit 23d03fd

Browse files
authored
Merge pull request #15 from frengor/dev
* Update to 0.1.1 * Fix the pessimistic quadratic behaviour of the Lins algorithm * [Add interior mutability to CounterMarker](#12) by @LegionMammal978 * Use [Iai](https://github.com/bheisler/iai) for running benchmarks on CI * Implement `Iterator` for `List` * Rename `all-non-nightly` feature to `full` * Put costly debug assertions behind the new `pedantic-debug-assertions` feature * Minor improvements and optimizations
2 parents 68cc73a + 4b5e1ea commit 23d03fd

22 files changed

+838
-722
lines changed

.github/workflows/bench.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Benchmark
2+
3+
on:
4+
pull_request:
5+
types: [labeled]
6+
7+
env:
8+
CARGO_TERM_COLOR: always
9+
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
10+
11+
jobs:
12+
bench:
13+
if: contains(github.event.pull_request.labels.*.name, 'run benchmarks')
14+
permissions:
15+
pull-requests: write
16+
env:
17+
WORKSPACE: ${{ github.workspace }}
18+
URL: ${{ github.event.pull_request.comments_url }}
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v3
22+
with:
23+
ref: ${{ github.event.pull_request.base.ref }}
24+
- uses: dtolnay/rust-toolchain@stable
25+
- name: Bench base branch
26+
run: |
27+
sudo apt-get update
28+
sudo apt-get install -y valgrind
29+
cargo update
30+
cargo bench -F full -q --bench bench > "${WORKSPACE}"/____old_results.txt
31+
- uses: actions/checkout@v3
32+
with:
33+
ref: ${{ github.event.pull_request.head.ref }}
34+
clean: false
35+
- name: Bench head branch
36+
run: |
37+
cargo update
38+
cargo bench -F full -q --bench bench > "${WORKSPACE}"/____results.txt
39+
- name: Write comment
40+
run: |
41+
{
42+
echo '<strong>Benchmark results:</strong>'
43+
echo ''
44+
cat "${WORKSPACE}"/____results.txt
45+
echo ''
46+
echo '<details><summary><strong>Old results:</strong></summary><p>'
47+
echo ''
48+
cat "${WORKSPACE}"/____old_results.txt
49+
echo '</p></details>'
50+
} > "${WORKSPACE}"/__result.txt
51+
- uses: thollander/[email protected]
52+
with:
53+
filePath: '__result.txt'
54+
mode: recreate
55+
- name: Remove label
56+
uses: actions-ecosystem/[email protected]
57+
with:
58+
labels: "run benchmarks"
Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
name: Build and Test nightly
1+
name: Build and Test
22

33
on: [push, pull_request]
44

55
env:
66
CARGO_TERM_COLOR: always
7+
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
78

89
jobs:
910
build:
@@ -12,13 +13,13 @@ jobs:
1213
- uses: actions/checkout@v3
1314
- uses: dtolnay/rust-toolchain@nightly
1415
- name: Build
15-
run: cargo build --verbose --workspace
16+
run: cargo build -F pedantic-debug-assertions --verbose --workspace
1617
- name: Run tests
17-
run: cargo test --verbose --workspace
18-
- name: Build with nightly feature
19-
run: cargo build -F nightly --verbose --workspace
20-
- name: Run tests with nightly feature
21-
run: cargo test -F nightly --verbose --workspace
18+
run: cargo test -F pedantic-debug-assertions --verbose --workspace
19+
- name: Build full
20+
run: cargo build -F full,pedantic-debug-assertions --verbose --workspace
21+
- name: Run full tests
22+
run: cargo test -F full,pedantic-debug-assertions --verbose --workspace
2223
- name: Build with every feature
2324
run: cargo build --all-features --verbose --workspace
2425
- name: Run tests with every feature

.github/workflows/buildStable.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Build on Stable
2+
3+
on: [push, pull_request]
4+
5+
env:
6+
CARGO_TERM_COLOR: always
7+
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v3
14+
- uses: dtolnay/rust-toolchain@stable
15+
- name: Build
16+
run: cargo build -F full,pedantic-debug-assertions --verbose --workspace

.github/workflows/miri.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
name: Run Miri
1+
name: Miri
22

33
on: [push, pull_request]
44

55
env:
66
CARGO_TERM_COLOR: always
7+
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
78

89
jobs:
910
build:
@@ -17,10 +18,8 @@ jobs:
1718
rustup override set nightly
1819
cargo miri setup
1920
- name: Run tests
20-
run: cargo miri test --verbose --workspace
21-
- name: Run tests with every non-nightly feature
22-
run: cargo miri test -F all-non-nightly --verbose --workspace
23-
- name: Run tests with nightly feature
24-
run: cargo miri test -F nightly --verbose --workspace
21+
run: cargo miri test -F pedantic-debug-assertions --verbose --workspace
22+
- name: Run full tests
23+
run: cargo miri test -F full,pedantic-debug-assertions --verbose --workspace
2524
- name: Run tests with every feature
2625
run: cargo miri test --all-features --verbose --workspace

.github/workflows/testStable.yml

Lines changed: 0 additions & 21 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,13 @@ Also, remember to open the pull requests toward the `dev` branch. The `main` bra
88

99
## The collection algorithm
1010

11-
The core idea behind the algorithm is the same as the one presented by Lins in ["Cyclic Reference Counting With Lazy Mark-Scan"](https://kar.kent.ac.uk/22347/1/CyclicLin.pdf).
11+
The core idea behind the algorithm is the same as the one presented by Lins in ["Cyclic Reference Counting With Lazy Mark-Scan"](https://kar.kent.ac.uk/22347/1/CyclicLin.pdf)
12+
and by Bacon and Rajan in ["Concurrent Cycle Collection in Reference Counted Systems"](https://pages.cs.wisc.edu/~cymen/misc/interests/Bacon01Concurrent.pdf).
1213
However, the implementation differs in order to make the collector faster and more resilient to random panics and failures in general.
1314

14-
> **N.B.:** `rust-cc` is *not* strictly an implementation of the algorithm shown in the linked paper and it's never been
15+
> **N.B.:** `rust-cc` is *not* strictly an implementation of the algorithm shown in the linked papers and it's never been
1516
> intended as such. Feel free to propose any kind of improvement!
1617
17-
> Also, a future update will provide the fix proposed by Bacon and Rajan in
18-
> ["Concurrent Cycle Collection in Reference Counted Systems"](https://pages.cs.wisc.edu/~cymen/misc/interests/Bacon01Concurrent.pdf)
19-
> in order to prevent the pessimistic quadratic behaviour of the Lins algorithm.
20-
> There are already some improvements, like it's not possible to have the same object twice into the list of possible
21-
> roots of cyclic garbage (the `POSSIBLE_CYCLES` list).
22-
2318
The following is a summarized version of the collection algorithm:
2419
When a `Cc` smart pointer is dropped, the reference count (RC) is decreased by 1. If it reaches 0, then the allocated
2520
object pointed by the `Cc` (called `CcOnHeap`) is dropped, otherwise the `Cc` is put into the `POSSIBLE_CYCLES` list.
@@ -28,12 +23,12 @@ Sometimes (see [`crate::trigger_collection`](./src/lib.rs)), when creating a new
2823
the objects inside the `POSSIBLE_CYCLES` list are checked to see if they are part of a garbage cycle.
2924

3025
Therefore, they undergo two tracing passes:
31-
- **Trace Counting:** during this phase, starting from the elements inside `POSSIBLE_CYCLES` (one at a time for now, see the note above),
26+
- **Trace Counting:** during this phase, starting from the elements inside `POSSIBLE_CYCLES`,
3227
objects are traced to count the amount of pointers to each `CcOnHeap` that are reachable from the list's `Cc`s.
3328
The `tracing_counter` "field" (see the [`counter_marker` module](./src/counter_marker.rs) for more info) is used to keep track of this number.
3429
<details>
3530
<summary>About tracing_counter</summary>
36-
<p>In the paper, Lins decrements the RC itself instead of using another counter. However, if during tracing there was a panic,
31+
<p>In the papers, Lins, Bacon and Rajan decrement the RC itself instead of using another counter. However, if during tracing there was a panic,
3732
it would be hard for `rust-cc` to restore the RC correctly. This is the reason for the choice of having another counter.
3833
The invariant regarding this second counter is that it must always be between 0 and RC inclusively.
3934
</p>

Cargo.toml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ edition.workspace = true
1414
members = ["derive"]
1515

1616
[workspace.package]
17-
version = "0.1.0"
17+
version = "0.1.1"
1818
authors = ["fren_gor <[email protected]>"]
1919
repository = "https://github.com/frengor/rust-cc"
2020
categories = ["memory-management"]
@@ -25,22 +25,18 @@ edition = "2021"
2525

2626
[features]
2727
default = ["auto-collect"]
28-
all-non-nightly = ["auto-collect"]
28+
full = ["auto-collect"]
2929
nightly = []
3030
auto-collect = []
31+
pedantic-debug-assertions = []
3132

3233
[dependencies]
3334
thiserror = "1.0.37"
3435

3536
[dev-dependencies]
36-
criterion = { version = "0.3.5", features = ["html_reports"] }
37+
iai = "0.1.1"
3738
rand = "0.8.3"
3839

3940
[[bench]]
4041
name = "bench"
4142
harness = false
42-
43-
[[bench]]
44-
name = "shredder_benchmark"
45-
harness = false
46-

benches/bench.rs

Lines changed: 11 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,15 @@
1-
use std::cell::RefCell;
2-
use std::hint::black_box;
1+
///! Same benchmarks of [rust-cc-benchmarks](https://github.com/frengor/rust-cc-benchmarks), but run with [iai](https://github.com/bheisler/iai).
32
4-
use criterion::{criterion_group, criterion_main, Criterion};
5-
use rust_cc::*;
6-
7-
fn benchmark(c: &mut Criterion) {
8-
c.bench_function("finalized", |b| {
9-
b.iter(|| finalized(black_box(1), black_box(1)))
10-
});
11-
/*c.bench_function("not_finalized", |b| {
12-
b.iter(|| not_finalized(black_box(2), black_box(2)))
13-
});*/
14-
}
15-
16-
criterion_group! {
17-
name = benches;
18-
config = Criterion::default().sample_size(20);
19-
targets = benchmark
20-
}
21-
criterion_main!(benches);
22-
23-
macro_rules! define_test {
24-
(fn $build_fn:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident) => {
25-
#[inline(always)]
26-
fn $build_fn(d_: u64, e_: i64) -> Cc<$A> {
27-
let root1 = Cc::new($A {
28-
c: RefCell::new(Some(Cc::new($C {
29-
d: Cc::new($D { _value: d_ }),
30-
b: Cc::new($B {
31-
e: Cc::new($E { _value: e_ }),
32-
b: RefCell::new(None),
33-
a: RefCell::new(None),
34-
}),
35-
}))),
36-
b: RefCell::new(None),
37-
});
38-
if let Some(ref c) = *root1.c.borrow_mut() {
39-
*root1.b.borrow_mut() = Some(c.b.clone());
40-
*c.b.b.borrow_mut() = Some(c.b.clone());
41-
*c.b.a.borrow_mut() = Some(root1.clone());
42-
}
43-
root1
44-
}
45-
46-
struct $A {
47-
c: RefCell<Option<Cc<$C>>>,
48-
b: RefCell<Option<Cc<$B>>>,
49-
}
50-
struct $B {
51-
e: Cc<$E>,
52-
b: RefCell<Option<Cc<$B>>>,
53-
a: RefCell<Option<Cc<$A>>>,
54-
}
55-
struct $C {
56-
d: Cc<$D>,
57-
b: Cc<$B>,
58-
}
59-
struct $D {
60-
_value: u64,
61-
}
62-
struct $E {
63-
_value: i64,
64-
}
65-
66-
unsafe impl Trace for $A {
67-
fn trace(&self, ctx: &mut Context<'_>) {
68-
if let Some(ref c) = *self.c.borrow() {
69-
c.trace(ctx);
70-
}
71-
if let Some(ref b) = *self.b.borrow() {
72-
b.trace(ctx);
73-
}
74-
}
75-
}
76-
77-
unsafe impl Trace for $B {
78-
fn trace(&self, ctx: &mut Context<'_>) {
79-
self.e.trace(ctx);
80-
if let Some(ref b) = *self.b.borrow() {
81-
b.trace(ctx);
82-
}
83-
if let Some(ref a) = *self.a.borrow() {
84-
a.trace(ctx);
85-
}
86-
}
87-
}
88-
89-
unsafe impl Trace for $C {
90-
fn trace(&self, ctx: &mut Context<'_>) {
91-
self.d.trace(ctx);
92-
self.b.trace(ctx);
93-
}
94-
}
95-
96-
unsafe impl Trace for $D {
97-
fn trace(&self, _: &mut Context<'_>) {}
98-
}
99-
100-
unsafe impl Trace for $E {
101-
fn trace(&self, _: &mut Context<'_>) {}
102-
}
103-
};
104-
(with_finalizers fn $build_fn:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident) => {
105-
define_test!(fn $build_fn, $A, $B, $C, $D, $E);
106-
107-
impl Finalize for $A {
108-
fn finalize(&self) {}
109-
}
110-
111-
impl Finalize for $B {
112-
fn finalize(&self) {}
113-
}
114-
115-
impl Finalize for $C {
116-
fn finalize(&self) {}
117-
}
118-
119-
impl Finalize for $D {
120-
fn finalize(&self) {}
121-
}
122-
123-
impl Finalize for $E {
124-
fn finalize(&self) {}
125-
}
126-
};
127-
}
128-
129-
#[inline(never)]
130-
fn finalized(d_: u64, e_: i64) {
131-
define_test!(with_finalizers fn build, A, B, C, D, E);
132-
133-
{
134-
let root1 = build(d_, e_);
135-
collect_cycles();
136-
let _root2 = root1.clone();
137-
collect_cycles();
138-
*root1.c.borrow_mut() = None;
139-
collect_cycles();
140-
}
141-
collect_cycles();
3+
mod benches {
4+
pub(super) mod stress_test;
5+
pub(super) mod binary_trees;
6+
pub(super) mod binary_trees_with_parent_pointers;
7+
pub(super) mod large_linked_list;
1428
}
1439

144-
// Objects are always finalized now
145-
/*#[inline(never)]
146-
fn not_finalized(d_: u64, e_: i64) {
147-
define_test!(fn build, A, B, C, D, E);
10+
use benches::stress_test::benchmark_stress_test;
11+
use benches::binary_trees::benchmark_count_binary_trees;
12+
use benches::binary_trees_with_parent_pointers::benchmark_count_binary_trees_with_parent;
13+
use benches::large_linked_list::benchmark_large_linked_list;
14814

149-
{
150-
let root1 = build(d_, e_);
151-
collect_cycles();
152-
let _root2 = root1.clone();
153-
collect_cycles();
154-
*root1.b.borrow_mut() = None;
155-
collect_cycles();
156-
}
157-
collect_cycles();
158-
}*/
15+
iai::main!(benchmark_stress_test, benchmark_count_binary_trees, benchmark_count_binary_trees_with_parent, benchmark_large_linked_list);

0 commit comments

Comments
 (0)