Skip to content

Commit

Permalink
Custom Allocation (#168)
Browse files Browse the repository at this point in the history
- perf: introduces a custom allocator to eradicate a lot of ref counting during execution, should go faster.
  • Loading branch information
gmorpheme authored Sep 15, 2021
1 parent 4e81983 commit bdcb5dc
Show file tree
Hide file tree
Showing 81 changed files with 6,368 additions and 2,979 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-rust.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:

benchmark:
needs: [check, test, fmt, clippy]
runs-on: ubuntu-latest
runs-on: macos-latest
# if: ${{ github.event_name == 'pull_request' }}
# if: ${{ false }}
continue-on-error: true
Expand Down
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ html5ever = "0.25.1"
lru = "0.6.5"
uuid = { version = "0.8.2", features = ["serde", "v4"] }
webbrowser = "0.5.5"
bitmaps = "3.1.0"
pretty-hex = "0.2.1"

[[bench]]
harness = false
Expand Down
170 changes: 129 additions & 41 deletions benches/alloc.rs
Original file line number Diff line number Diff line change
@@ -1,86 +1,174 @@
//! STG allocation benchmarks

use eucalypt::common::sourcemap::Smid;
use eucalypt::eval::machines::stg::env::*;
use eucalypt::eval::machines::stg::syntax::dsl;
use eucalypt::eval::machines::stg::syntax::LambdaForm;
use std::iter;

use eucalypt::{
common::sourcemap::Smid,
eval::{
machine::{
env::{Closure, EnvFrame},
env_builder::EnvBuilder,
},
memory::{
alloc::ScopedAllocator,
array::Array,
heap::Heap,
mutator::MutatorHeapView,
syntax::{LambdaForm, Native, Ref, RefPtr, StgBuilder},
},
stg::tags::DataConstructor,
},
};

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

use std::{cell::RefCell, iter, rc::Rc};
fn box_one<'guard>(view: MutatorHeapView<'guard>, empty: RefPtr<EnvFrame>) -> RefPtr<Closure> {
view.alloc(Closure::new(
view.data(
DataConstructor::BoxedString.tag(),
Array::from_slice(&view, &[Ref::V(Native::Num(1.into()))]),
)
.unwrap()
.as_ptr(),
empty,
))
.unwrap()
.as_ptr()
}

fn identity<'guard>(view: MutatorHeapView<'guard>, empty: RefPtr<EnvFrame>) -> RefPtr<Closure> {
view.alloc(Closure::close(
&LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default()),
empty,
))
.unwrap()
.as_ptr()
}

fn fake_bindings(width: usize) -> Vec<LambdaForm> {
iter::repeat_with(|| dsl::lambda(1, dsl::local(0)))
.take(width)
.collect()
fn fake_bindings<'guard>(view: MutatorHeapView<'guard>, width: usize) -> Vec<LambdaForm> {
iter::repeat_with(|| {
LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default())
})
.take(width)
.collect()
}

fn fake_env_stack(width: usize, height: usize) -> Rc<EnvFrame> {
let mut base = Rc::new(EnvFrame::empty());
fn fake_env_stack<'guard>(
view: MutatorHeapView<'guard>,
empty: RefPtr<EnvFrame>,
width: usize,
height: usize,
) -> RefPtr<EnvFrame> {
let mut base = empty;

for _ in 0..height {
let bindings = fake_bindings(width);
base = EnvFrame::from_letrec(&bindings, &base, Smid::default());
let bindings = fake_bindings(view, width);
base = view.from_letrec(&bindings, base, Smid::default());
}

base
}

/// Allocate a letrec of identify function bindings
fn alloc_let(bindings: &[LambdaForm]) -> Rc<EnvFrame> {
EnvFrame::from_let(bindings, &Rc::new(EnvFrame::empty()), Smid::default())
fn alloc_let<'guard>(
view: MutatorHeapView<'guard>,
empty: RefPtr<EnvFrame>,
bindings: &[LambdaForm],
) -> RefPtr<EnvFrame> {
view.from_let(bindings, empty, Smid::default())
}

/// Allocate a letrec of identify function bindings
fn alloc_letrec(bindings: &[LambdaForm]) -> Rc<EnvFrame> {
EnvFrame::from_letrec(bindings, &Rc::new(EnvFrame::empty()), Smid::default())
fn alloc_letrec<'guard>(
view: MutatorHeapView<'guard>,
empty: RefPtr<EnvFrame>,
bindings: &[LambdaForm],
) -> RefPtr<EnvFrame> {
view.from_letrec(bindings, empty, Smid::default())
}

/// Access deep closure
fn access(env: &Rc<EnvFrame>, depth: usize) -> Option<&RefCell<Closure>> {
env.get(depth)
fn access<'guard>(
view: MutatorHeapView<'guard>,
env: RefPtr<EnvFrame>,
depth: usize,
) -> Option<RefPtr<Closure>> {
let e = view.scoped(env);
(*e).get(&view, depth)
}

/// Update deep closure with a new value
fn update(env: &Rc<EnvFrame>, depth: usize) {
let value = Closure::new(dsl::box_num(1), Rc::new(EnvFrame::empty()));
env.update(depth, value).unwrap();
fn update<'guard>(
view: MutatorHeapView<'guard>,
empty: RefPtr<EnvFrame>,
env: RefPtr<EnvFrame>,
depth: usize,
) {
let e = view.scoped(env);
let value = box_one(view, empty);
(*e).update(&view, depth, value).unwrap();
}

/// Create an identity lambda and saturate it
fn create_and_saturate_lambda() {
let mut lambda = Closure::close(&dsl::lambda(1, dsl::local(0)), &Rc::new(EnvFrame::empty()));
let args = vec![Closure::new(dsl::box_num(1), Rc::new(EnvFrame::empty()))];
lambda.saturate(args)
fn create_and_saturate_lambda<'guard>(view: MutatorHeapView<'guard>, empty: RefPtr<EnvFrame>) {
let mut lambda = view
.alloc(Closure::close(
&LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default()),
empty,
))
.unwrap()
.as_ptr();
let args = vec![box_one(view, empty)];
view.saturate(&*view.scoped(lambda), args.as_slice())
.unwrap();
}

/// Create an identity lambda and saturate it
fn create_partially_apply_and_saturate_lambda() {
let mut lambda = Closure::close(&dsl::lambda(2, dsl::local(0)), &Rc::new(EnvFrame::empty()));
let first_arg = vec![Closure::new(dsl::box_num(1), Rc::new(EnvFrame::empty()))];
let second_arg = vec![Closure::new(dsl::box_num(2), Rc::new(EnvFrame::empty()))];

lambda.partially_apply(first_arg);
lambda.saturate(second_arg);
fn create_partially_apply_and_saturate_lambda<'guard>(
view: MutatorHeapView<'guard>,
empty: RefPtr<EnvFrame>,
) {
let mut lambda = view
.alloc(Closure::close(
&LambdaForm::new(2, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default()),
empty,
))
.unwrap()
.as_ptr();
let first_arg = vec![box_one(view, empty)];
let second_arg = vec![box_one(view, empty)];

let lambda = view
.partially_apply(&*view.scoped(lambda), first_arg.as_slice())
.unwrap();
view.saturate(&*view.scoped(lambda), second_arg.as_slice())
.unwrap();
}

fn criterion_benchmark(c: &mut Criterion) {
let bindings = fake_bindings(10);
let env_stack = fake_env_stack(20, 4);
c.bench_function("alloc_let", |b| b.iter(|| alloc_let(&bindings)));
c.bench_function("alloc_letrec", |b| b.iter(|| alloc_letrec(&bindings)));
let heap = Heap::new();
let view = MutatorHeapView::new(&heap);
let empty = view.alloc(EnvFrame::default()).unwrap().as_ptr();
let bindings = fake_bindings(view, 10);
let env_stack = fake_env_stack(view, empty, 20, 4);
c.bench_function("alloc_let", |b| {
b.iter(|| alloc_let(view, empty, &bindings))
});
c.bench_function("alloc_letrec", |b| {
b.iter(|| alloc_letrec(view, empty, &bindings))
});
c.bench_function("deep_env_access", |b| {
b.iter(|| access(&env_stack, black_box(73)))
b.iter(|| access(view, env_stack, black_box(73)))
});
c.bench_function("deep_env_update", |b| {
b.iter(|| update(&env_stack, black_box(73)))
b.iter(|| update(view, empty, env_stack, black_box(73)))
});

c.bench_function("create_and_saturate_lambda", |b| {
b.iter(create_and_saturate_lambda)
b.iter(|| create_and_saturate_lambda(view, empty))
});
c.bench_function("create_partially_apply_and_saturate_lambda", |b| {
b.iter(create_partially_apply_and_saturate_lambda)
b.iter(|| create_partially_apply_and_saturate_lambda(view, empty))
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/core/cook/fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn fill_gaps(xs: &[RcExpr]) -> (Vec<RcExpr>, AnaphorSet) {

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

if let Some(end) = xs.last() {
naked_anaphora.extend(anaphora::naked_anaphora(&end));
naked_anaphora.extend(anaphora::naked_anaphora(end));
out.push(end.clone());
if let Some(suffix) = filler(Some(end), None) {
naked_anaphora.extend(anaphora::naked_anaphora(&suffix));
Expand Down
2 changes: 1 addition & 1 deletion src/core/cook/fixity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Distributor {
Ok(ret)
}
Expr::Var(_, Free(fv)) => {
if let Some((smid, fixity, precedence)) = self.env.get(&fv) {
if let Some((smid, fixity, precedence)) = self.env.get(fv) {
Ok(RcExpr::from(Expr::Operator(
*smid,
*fixity,
Expand Down
18 changes: 9 additions & 9 deletions src/core/desugar/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ fn declaration_to_binding(
decl: &Declaration,
desugarer: &mut Desugarer,
) -> Result<(Binder<String>, Embed<RcExpr>), CoreError> {
desugarer.push(&decl.name().name());
desugarer.push(decl.name().name());

let is_op = decl.is_operator();
let unary_fixity = static_fixity(&decl);
let unary_fixity = static_fixity(decl);
let components = DeclarationComponents::from(decl);

// Read metadata up front as it might affect translation of the
Expand Down Expand Up @@ -275,17 +275,17 @@ fn desugar_name(name: &Name, desugarer: &mut Desugarer) -> RcExpr {
Name::Normal(s, n) => {
if n.starts_with("__") && n.chars().nth(2).map_or(false, |c| c.is_uppercase()) {
RcExpr::from(Expr::Intrinsic(desugarer.new_smid(*s), n[2..].to_string()))
} else if BLOCK_ANAPHORA.is_anaphor(&n) {
} else if BLOCK_ANAPHORA.is_anaphor(n) {
let smid = desugarer.new_smid(*s);
RcExpr::from(Expr::BlockAnaphor(
smid,
BLOCK_ANAPHORA.to_explicit_anaphor(smid, &n),
BLOCK_ANAPHORA.to_explicit_anaphor(smid, n),
))
} else if EXPR_ANAPHORA.is_anaphor(&n) {
} else if EXPR_ANAPHORA.is_anaphor(n) {
let smid = desugarer.new_smid(*s);
RcExpr::from(Expr::ExprAnaphor(
smid,
EXPR_ANAPHORA.to_explicit_anaphor(smid, &n),
EXPR_ANAPHORA.to_explicit_anaphor(smid, n),
))
} else {
RcExpr::from(Expr::Name(desugarer.new_smid(*s), n.to_string()))
Expand Down Expand Up @@ -328,7 +328,7 @@ fn desugar_soup(
// simple static lookup
soup.pop();
if let Some(dlet) = soup.pop() {
soup.push(dlet.rebody(core::var(*s, desugarer.var(&n))));
soup.push(dlet.rebody(core::var(*s, desugarer.var(n))));
} else {
panic!("Expected default let");
}
Expand All @@ -338,7 +338,7 @@ fn desugar_soup(
soup.push(expr.clone());
lookup = PendingLookup::None;
} else {
soup.push(core::var(*s, desugarer.var(&n)))
soup.push(core::var(*s, desugarer.var(n)))
}
}
Expr::Operator(s, _, _, ref body) => {
Expand Down Expand Up @@ -495,7 +495,7 @@ pub fn desugar_string_pattern(

// if there are any anaphora wrap into a lambda
if desugarer.has_pending_string_anaphora() {
let binders = anaphora::to_binding_pattern(&desugarer.pending_string_anaphora())?;
let binders = anaphora::to_binding_pattern(desugarer.pending_string_anaphora())?;
desugarer.clear_pending_string_anaphora();
expr = core::lam(expr.smid(), binders, succ::succ(&expr)?);
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/desugar/desugarer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl<'smap> Desugarer<'smap> {
pub fn varify(&mut self, expr: RcExpr) -> RcExpr {
match &*expr.inner {
Expr::Name(smid, name) => {
let var = self.var(&name);
let var = self.var(name);
RcExpr::from(Expr::Var(*smid, moniker::Var::Free(var)))
}
_ => expr,
Expand Down
4 changes: 2 additions & 2 deletions src/core/export/embed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Embed for CoreExpr {
}
Expr::Intrinsic(_, n) => {
elements.push(lit(sym("c-bif")));
elements.push(lit(sym(&n)));
elements.push(lit(sym(n)));
}
Expr::Literal(_, x) => {
elements.push(lit(sym("c-lit")));
Expand Down Expand Up @@ -176,7 +176,7 @@ impl Embed for Primitive {
fn embed(&self) -> Expression {
match &*self {
Primitive::Str(s) => lit(str(&s)),
Primitive::Sym(s) => lit(sym(&s)),
Primitive::Sym(s) => lit(sym(s)),
Primitive::Num(n) => lit(num(n.clone())),
Primitive::Bool(b) => list(vec![
lit(sym("c-bool")),
Expand Down
Loading

0 comments on commit bdcb5dc

Please sign in to comment.