Skip to content

Commit bdcb5dc

Browse files
authored
Custom Allocation (#168)
- perf: introduces a custom allocator to eradicate a lot of ref counting during execution, should go faster.
1 parent 4e81983 commit bdcb5dc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+6368
-2979
lines changed

.github/workflows/build-rust.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ jobs:
8282

8383
benchmark:
8484
needs: [check, test, fmt, clippy]
85-
runs-on: ubuntu-latest
85+
runs-on: macos-latest
8686
# if: ${{ github.event_name == 'pull_request' }}
8787
# if: ${{ false }}
8888
continue-on-error: true

Cargo.lock

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ html5ever = "0.25.1"
4040
lru = "0.6.5"
4141
uuid = { version = "0.8.2", features = ["serde", "v4"] }
4242
webbrowser = "0.5.5"
43+
bitmaps = "3.1.0"
44+
pretty-hex = "0.2.1"
4345

4446
[[bench]]
4547
harness = false

benches/alloc.rs

+129-41
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,174 @@
11
//! STG allocation benchmarks
22
3-
use eucalypt::common::sourcemap::Smid;
4-
use eucalypt::eval::machines::stg::env::*;
5-
use eucalypt::eval::machines::stg::syntax::dsl;
6-
use eucalypt::eval::machines::stg::syntax::LambdaForm;
3+
use std::iter;
4+
5+
use eucalypt::{
6+
common::sourcemap::Smid,
7+
eval::{
8+
machine::{
9+
env::{Closure, EnvFrame},
10+
env_builder::EnvBuilder,
11+
},
12+
memory::{
13+
alloc::ScopedAllocator,
14+
array::Array,
15+
heap::Heap,
16+
mutator::MutatorHeapView,
17+
syntax::{LambdaForm, Native, Ref, RefPtr, StgBuilder},
18+
},
19+
stg::tags::DataConstructor,
20+
},
21+
};
722

823
use criterion::{black_box, criterion_group, criterion_main, Criterion};
924

10-
use std::{cell::RefCell, iter, rc::Rc};
25+
fn box_one<'guard>(view: MutatorHeapView<'guard>, empty: RefPtr<EnvFrame>) -> RefPtr<Closure> {
26+
view.alloc(Closure::new(
27+
view.data(
28+
DataConstructor::BoxedString.tag(),
29+
Array::from_slice(&view, &[Ref::V(Native::Num(1.into()))]),
30+
)
31+
.unwrap()
32+
.as_ptr(),
33+
empty,
34+
))
35+
.unwrap()
36+
.as_ptr()
37+
}
38+
39+
fn identity<'guard>(view: MutatorHeapView<'guard>, empty: RefPtr<EnvFrame>) -> RefPtr<Closure> {
40+
view.alloc(Closure::close(
41+
&LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default()),
42+
empty,
43+
))
44+
.unwrap()
45+
.as_ptr()
46+
}
1147

12-
fn fake_bindings(width: usize) -> Vec<LambdaForm> {
13-
iter::repeat_with(|| dsl::lambda(1, dsl::local(0)))
14-
.take(width)
15-
.collect()
48+
fn fake_bindings<'guard>(view: MutatorHeapView<'guard>, width: usize) -> Vec<LambdaForm> {
49+
iter::repeat_with(|| {
50+
LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default())
51+
})
52+
.take(width)
53+
.collect()
1654
}
1755

18-
fn fake_env_stack(width: usize, height: usize) -> Rc<EnvFrame> {
19-
let mut base = Rc::new(EnvFrame::empty());
56+
fn fake_env_stack<'guard>(
57+
view: MutatorHeapView<'guard>,
58+
empty: RefPtr<EnvFrame>,
59+
width: usize,
60+
height: usize,
61+
) -> RefPtr<EnvFrame> {
62+
let mut base = empty;
2063

2164
for _ in 0..height {
22-
let bindings = fake_bindings(width);
23-
base = EnvFrame::from_letrec(&bindings, &base, Smid::default());
65+
let bindings = fake_bindings(view, width);
66+
base = view.from_letrec(&bindings, base, Smid::default());
2467
}
2568

2669
base
2770
}
2871

2972
/// Allocate a letrec of identify function bindings
30-
fn alloc_let(bindings: &[LambdaForm]) -> Rc<EnvFrame> {
31-
EnvFrame::from_let(bindings, &Rc::new(EnvFrame::empty()), Smid::default())
73+
fn alloc_let<'guard>(
74+
view: MutatorHeapView<'guard>,
75+
empty: RefPtr<EnvFrame>,
76+
bindings: &[LambdaForm],
77+
) -> RefPtr<EnvFrame> {
78+
view.from_let(bindings, empty, Smid::default())
3279
}
3380

3481
/// Allocate a letrec of identify function bindings
35-
fn alloc_letrec(bindings: &[LambdaForm]) -> Rc<EnvFrame> {
36-
EnvFrame::from_letrec(bindings, &Rc::new(EnvFrame::empty()), Smid::default())
82+
fn alloc_letrec<'guard>(
83+
view: MutatorHeapView<'guard>,
84+
empty: RefPtr<EnvFrame>,
85+
bindings: &[LambdaForm],
86+
) -> RefPtr<EnvFrame> {
87+
view.from_letrec(bindings, empty, Smid::default())
3788
}
3889

3990
/// Access deep closure
40-
fn access(env: &Rc<EnvFrame>, depth: usize) -> Option<&RefCell<Closure>> {
41-
env.get(depth)
91+
fn access<'guard>(
92+
view: MutatorHeapView<'guard>,
93+
env: RefPtr<EnvFrame>,
94+
depth: usize,
95+
) -> Option<RefPtr<Closure>> {
96+
let e = view.scoped(env);
97+
(*e).get(&view, depth)
4298
}
4399

44100
/// Update deep closure with a new value
45-
fn update(env: &Rc<EnvFrame>, depth: usize) {
46-
let value = Closure::new(dsl::box_num(1), Rc::new(EnvFrame::empty()));
47-
env.update(depth, value).unwrap();
101+
fn update<'guard>(
102+
view: MutatorHeapView<'guard>,
103+
empty: RefPtr<EnvFrame>,
104+
env: RefPtr<EnvFrame>,
105+
depth: usize,
106+
) {
107+
let e = view.scoped(env);
108+
let value = box_one(view, empty);
109+
(*e).update(&view, depth, value).unwrap();
48110
}
49111

50112
/// Create an identity lambda and saturate it
51-
fn create_and_saturate_lambda() {
52-
let mut lambda = Closure::close(&dsl::lambda(1, dsl::local(0)), &Rc::new(EnvFrame::empty()));
53-
let args = vec![Closure::new(dsl::box_num(1), Rc::new(EnvFrame::empty()))];
54-
lambda.saturate(args)
113+
fn create_and_saturate_lambda<'guard>(view: MutatorHeapView<'guard>, empty: RefPtr<EnvFrame>) {
114+
let mut lambda = view
115+
.alloc(Closure::close(
116+
&LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default()),
117+
empty,
118+
))
119+
.unwrap()
120+
.as_ptr();
121+
let args = vec![box_one(view, empty)];
122+
view.saturate(&*view.scoped(lambda), args.as_slice())
123+
.unwrap();
55124
}
56125

57126
/// Create an identity lambda and saturate it
58-
fn create_partially_apply_and_saturate_lambda() {
59-
let mut lambda = Closure::close(&dsl::lambda(2, dsl::local(0)), &Rc::new(EnvFrame::empty()));
60-
let first_arg = vec![Closure::new(dsl::box_num(1), Rc::new(EnvFrame::empty()))];
61-
let second_arg = vec![Closure::new(dsl::box_num(2), Rc::new(EnvFrame::empty()))];
62-
63-
lambda.partially_apply(first_arg);
64-
lambda.saturate(second_arg);
127+
fn create_partially_apply_and_saturate_lambda<'guard>(
128+
view: MutatorHeapView<'guard>,
129+
empty: RefPtr<EnvFrame>,
130+
) {
131+
let mut lambda = view
132+
.alloc(Closure::close(
133+
&LambdaForm::new(2, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default()),
134+
empty,
135+
))
136+
.unwrap()
137+
.as_ptr();
138+
let first_arg = vec![box_one(view, empty)];
139+
let second_arg = vec![box_one(view, empty)];
140+
141+
let lambda = view
142+
.partially_apply(&*view.scoped(lambda), first_arg.as_slice())
143+
.unwrap();
144+
view.saturate(&*view.scoped(lambda), second_arg.as_slice())
145+
.unwrap();
65146
}
66147

67148
fn criterion_benchmark(c: &mut Criterion) {
68-
let bindings = fake_bindings(10);
69-
let env_stack = fake_env_stack(20, 4);
70-
c.bench_function("alloc_let", |b| b.iter(|| alloc_let(&bindings)));
71-
c.bench_function("alloc_letrec", |b| b.iter(|| alloc_letrec(&bindings)));
149+
let heap = Heap::new();
150+
let view = MutatorHeapView::new(&heap);
151+
let empty = view.alloc(EnvFrame::default()).unwrap().as_ptr();
152+
let bindings = fake_bindings(view, 10);
153+
let env_stack = fake_env_stack(view, empty, 20, 4);
154+
c.bench_function("alloc_let", |b| {
155+
b.iter(|| alloc_let(view, empty, &bindings))
156+
});
157+
c.bench_function("alloc_letrec", |b| {
158+
b.iter(|| alloc_letrec(view, empty, &bindings))
159+
});
72160
c.bench_function("deep_env_access", |b| {
73-
b.iter(|| access(&env_stack, black_box(73)))
161+
b.iter(|| access(view, env_stack, black_box(73)))
74162
});
75163
c.bench_function("deep_env_update", |b| {
76-
b.iter(|| update(&env_stack, black_box(73)))
164+
b.iter(|| update(view, empty, env_stack, black_box(73)))
77165
});
78166

79167
c.bench_function("create_and_saturate_lambda", |b| {
80-
b.iter(create_and_saturate_lambda)
168+
b.iter(|| create_and_saturate_lambda(view, empty))
81169
});
82170
c.bench_function("create_partially_apply_and_saturate_lambda", |b| {
83-
b.iter(create_partially_apply_and_saturate_lambda)
171+
b.iter(|| create_partially_apply_and_saturate_lambda(view, empty))
84172
});
85173
}
86174

src/core/cook/fill.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn fill_gaps(xs: &[RcExpr]) -> (Vec<RcExpr>, AnaphorSet) {
1919

2020
for window in xs.windows(2) {
2121
if let [ref l, ref r] = window {
22-
naked_anaphora.extend(anaphora::naked_anaphora(&l));
22+
naked_anaphora.extend(anaphora::naked_anaphora(l));
2323
out.push(l.clone());
2424
if let Some(fill) = filler(Some(l), Some(r)) {
2525
naked_anaphora.extend(anaphora::naked_anaphora(&fill));
@@ -29,7 +29,7 @@ pub fn fill_gaps(xs: &[RcExpr]) -> (Vec<RcExpr>, AnaphorSet) {
2929
}
3030

3131
if let Some(end) = xs.last() {
32-
naked_anaphora.extend(anaphora::naked_anaphora(&end));
32+
naked_anaphora.extend(anaphora::naked_anaphora(end));
3333
out.push(end.clone());
3434
if let Some(suffix) = filler(Some(end), None) {
3535
naked_anaphora.extend(anaphora::naked_anaphora(&suffix));

src/core/cook/fixity.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl Distributor {
9696
Ok(ret)
9797
}
9898
Expr::Var(_, Free(fv)) => {
99-
if let Some((smid, fixity, precedence)) = self.env.get(&fv) {
99+
if let Some((smid, fixity, precedence)) = self.env.get(fv) {
100100
Ok(RcExpr::from(Expr::Operator(
101101
*smid,
102102
*fixity,

src/core/desugar/ast.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ fn declaration_to_binding(
4848
decl: &Declaration,
4949
desugarer: &mut Desugarer,
5050
) -> Result<(Binder<String>, Embed<RcExpr>), CoreError> {
51-
desugarer.push(&decl.name().name());
51+
desugarer.push(decl.name().name());
5252

5353
let is_op = decl.is_operator();
54-
let unary_fixity = static_fixity(&decl);
54+
let unary_fixity = static_fixity(decl);
5555
let components = DeclarationComponents::from(decl);
5656

5757
// Read metadata up front as it might affect translation of the
@@ -275,17 +275,17 @@ fn desugar_name(name: &Name, desugarer: &mut Desugarer) -> RcExpr {
275275
Name::Normal(s, n) => {
276276
if n.starts_with("__") && n.chars().nth(2).map_or(false, |c| c.is_uppercase()) {
277277
RcExpr::from(Expr::Intrinsic(desugarer.new_smid(*s), n[2..].to_string()))
278-
} else if BLOCK_ANAPHORA.is_anaphor(&n) {
278+
} else if BLOCK_ANAPHORA.is_anaphor(n) {
279279
let smid = desugarer.new_smid(*s);
280280
RcExpr::from(Expr::BlockAnaphor(
281281
smid,
282-
BLOCK_ANAPHORA.to_explicit_anaphor(smid, &n),
282+
BLOCK_ANAPHORA.to_explicit_anaphor(smid, n),
283283
))
284-
} else if EXPR_ANAPHORA.is_anaphor(&n) {
284+
} else if EXPR_ANAPHORA.is_anaphor(n) {
285285
let smid = desugarer.new_smid(*s);
286286
RcExpr::from(Expr::ExprAnaphor(
287287
smid,
288-
EXPR_ANAPHORA.to_explicit_anaphor(smid, &n),
288+
EXPR_ANAPHORA.to_explicit_anaphor(smid, n),
289289
))
290290
} else {
291291
RcExpr::from(Expr::Name(desugarer.new_smid(*s), n.to_string()))
@@ -328,7 +328,7 @@ fn desugar_soup(
328328
// simple static lookup
329329
soup.pop();
330330
if let Some(dlet) = soup.pop() {
331-
soup.push(dlet.rebody(core::var(*s, desugarer.var(&n))));
331+
soup.push(dlet.rebody(core::var(*s, desugarer.var(n))));
332332
} else {
333333
panic!("Expected default let");
334334
}
@@ -338,7 +338,7 @@ fn desugar_soup(
338338
soup.push(expr.clone());
339339
lookup = PendingLookup::None;
340340
} else {
341-
soup.push(core::var(*s, desugarer.var(&n)))
341+
soup.push(core::var(*s, desugarer.var(n)))
342342
}
343343
}
344344
Expr::Operator(s, _, _, ref body) => {
@@ -495,7 +495,7 @@ pub fn desugar_string_pattern(
495495

496496
// if there are any anaphora wrap into a lambda
497497
if desugarer.has_pending_string_anaphora() {
498-
let binders = anaphora::to_binding_pattern(&desugarer.pending_string_anaphora())?;
498+
let binders = anaphora::to_binding_pattern(desugarer.pending_string_anaphora())?;
499499
desugarer.clear_pending_string_anaphora();
500500
expr = core::lam(expr.smid(), binders, succ::succ(&expr)?);
501501
}

src/core/desugar/desugarer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ impl<'smap> Desugarer<'smap> {
175175
pub fn varify(&mut self, expr: RcExpr) -> RcExpr {
176176
match &*expr.inner {
177177
Expr::Name(smid, name) => {
178-
let var = self.var(&name);
178+
let var = self.var(name);
179179
RcExpr::from(Expr::Var(*smid, moniker::Var::Free(var)))
180180
}
181181
_ => expr,

src/core/export/embed.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl Embed for CoreExpr {
4848
}
4949
Expr::Intrinsic(_, n) => {
5050
elements.push(lit(sym("c-bif")));
51-
elements.push(lit(sym(&n)));
51+
elements.push(lit(sym(n)));
5252
}
5353
Expr::Literal(_, x) => {
5454
elements.push(lit(sym("c-lit")));
@@ -176,7 +176,7 @@ impl Embed for Primitive {
176176
fn embed(&self) -> Expression {
177177
match &*self {
178178
Primitive::Str(s) => lit(str(&s)),
179-
Primitive::Sym(s) => lit(sym(&s)),
179+
Primitive::Sym(s) => lit(sym(s)),
180180
Primitive::Num(n) => lit(num(n.clone())),
181181
Primitive::Bool(b) => list(vec![
182182
lit(sym("c-bool")),

0 commit comments

Comments
 (0)