Skip to content

Commit

Permalink
Fix large object allocation (#175)
Browse files Browse the repository at this point in the history
  • Loading branch information
gmorpheme authored Dec 14, 2021
1 parent 1c2b4a8 commit 51729d0
Show file tree
Hide file tree
Showing 12 changed files with 367 additions and 237 deletions.
372 changes: 224 additions & 148 deletions Cargo.lock

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,39 @@ authors = ["gmorpheme <github@gmorpheme.net>"]
edition = "2018"

[build-dependencies]
lalrpop = { version = "0.19.5", features = ["lexer"] }
lalrpop = { version = "0.19.6", features = ["lexer"] }

[dev-dependencies]
criterion = "0.3"
criterion = "0.3.5"

[dependencies]
regex = "1.4.5"
matches = "0.1.8"
regex = "1.5.4"
matches = "0.1.9"
codespan = "0.11.1"
codespan-reporting = "0.11.1"
lalrpop-util = "0.19.5"
serde_json = "1.0.64"
lalrpop-util = "0.19.6"
serde_json = "1.0.72"
yaml-rust = { git = "https://github.com/curvelogic/yaml-rust", rev = "e3a9b432c43fcf9a19f1836657091caf7ae3b15f" }
unic-ucd-category = "0.9.0"
itertools = "0.10.0"
thiserror = "1.0.24"
structopt = "0.3.21"
url = "2.2.1"
itertools = "0.10.1"
thiserror = "1.0.30"
structopt = "0.3.25"
url = "2.2.2"
pretty = "0.10.0"
moniker = { git = "https://github.com/curvelogic/moniker", rev = "c620b2a69d6cad4724cba07775ed65a7ae556c9c" }
indexmap = "1.6.2"
petgraph = "0.5.1"
indexmap = "1.7.0"
petgraph = "0.6.0"
atty = "0.2"
csv = "1.1.6"
lazy_static = "1.4.0"
chrono = "0.4.19"
bitflags = "1.2.1"
bitflags = "1.3.2"
quick-xml = "0.22.0"
chrono-tz = "0.5.3"
chrono-tz = "0.6.0"
toml = { version = "0.5.8", features = ["preserve_order"] }
dirs = "3.0.2"
html5ever = "0.25.1"
lru = "0.6.5"
lru = "0.7.0"
uuid = { version = "0.8.2", features = ["serde", "v4"] }
webbrowser = "0.5.5"
bitmaps = "3.1.0"
Expand Down
7 changes: 1 addition & 6 deletions src/common/sourcemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,11 @@ pub struct SourceInfo {
}

/// Store all source info...
#[derive(Default)]
pub struct SourceMap {
source: Vec<SourceInfo>,
}

impl Default for SourceMap {
fn default() -> Self {
Self { source: vec![] }
}
}

impl SourceMap {
/// Create a new, empty database of files.
pub fn new() -> Self {
Expand Down
9 changes: 1 addition & 8 deletions src/core/cook/fixity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,11 @@ type Env = SimpleEnvironment<FreeVar<String>, OpMeta>;

/// Distribute maintains state as we traverse through the tree
/// accumulating correspondence between names and operator metadata
#[derive(Default)]
pub struct Distributor {
env: Env,
}

impl Default for Distributor {
fn default() -> Self {
Distributor {
env: Env::default(),
}
}
}

impl Distributor {
/// If an expression defines an operator, strip off (and return)
/// the operator metadata and expose the operator callable.
Expand Down
11 changes: 1 addition & 10 deletions src/core/cook/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub fn cook(expr: RcExpr) -> Result<RcExpr, CoreError> {
///
/// Needs to track whether we are within the scope of an
/// expression-anaophoric lambda.
#[derive(Default)]
pub struct Cooker {
/// True when we have traversed into the scope of expression anaphora
in_expr_anaphor_scope: bool,
Expand All @@ -31,16 +32,6 @@ pub struct Cooker {
pending_block_anaphora: HashMap<Anaphor<Smid, i32>, FreeVar<String>>,
}

impl Default for Cooker {
fn default() -> Self {
Cooker {
in_expr_anaphor_scope: false,
pending_expr_anaphora: HashMap::new(),
pending_block_anaphora: HashMap::new(),
}
}
}

impl Cooker {
/// Cook the expression `expr` resolving all operators and
/// eliminating Expr::Soup in favour of hierarchical expressions.
Expand Down
9 changes: 5 additions & 4 deletions src/core/inline/reduce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::core::expr::*;
use crate::core::transform::succ;
use moniker::*;

#[allow(clippy::redundant_closure)]
pub fn inline_pass(expr: &RcExpr) -> Result<RcExpr, CoreError> {
distribute(expr).and_then(|ref e| beta_reduce(e))
}
Expand Down Expand Up @@ -75,10 +76,10 @@ fn beta_reduce(expr: &RcExpr) -> Result<RcExpr, CoreError> {
// args for now
expr.walk_safe(&mut |e| beta_reduce(&e))
} else {
let args =
xs.iter()
.map(|arg| beta_reduce(arg))
.collect::<Result<Vec<RcExpr>, CoreError>>()?;
let args = xs
.iter()
.map(beta_reduce)
.collect::<Result<Vec<RcExpr>, CoreError>>()?;

let mappings = <_>::zip(binders.into_iter(), args).collect::<Vec<_>>();

Expand Down
22 changes: 6 additions & 16 deletions src/core/simplify/prune.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,23 +116,13 @@ impl RcMarkExpr {
}
}

#[derive(Debug)]
#[derive(Debug, Default)]
pub struct ScopeTracker<'expr> {
scopes: VecDeque<&'expr RcMarkExpr>,
reachable: bool,
marked_count: usize,
}

impl<'expr> Default for ScopeTracker<'expr> {
fn default() -> Self {
ScopeTracker {
scopes: VecDeque::new(),
reachable: false,
marked_count: 0,
}
}
}

impl<'expr> ScopeTracker<'expr> {
pub fn mark_reachable(&mut self, expr: &'expr RcMarkExpr) {
Self::mark_body(expr);
Expand Down Expand Up @@ -320,9 +310,9 @@ impl<'expr> ScopeTracker<'expr> {
*s,
Self::blank_unseen(e),
n.clone(),
fb.as_ref().map(|x| Self::blank_unseen(x)),
fb.as_ref().map(Self::blank_unseen),
),
Expr::List(s, xs) => Expr::List(*s, xs.iter().map(|x| Self::blank_unseen(x)).collect()),
Expr::List(s, xs) => Expr::List(*s, xs.iter().map(Self::blank_unseen).collect()),
Expr::Block(s, block_map) => Expr::Block(
*s,
block_map
Expand All @@ -332,14 +322,14 @@ impl<'expr> ScopeTracker<'expr> {
),
Expr::Meta(s, e, m) => Expr::Meta(*s, Self::blank_unseen(e), Self::blank_unseen(m)),
Expr::ArgTuple(s, xs) => {
Expr::ArgTuple(*s, xs.iter().map(|x| Self::blank_unseen(x)).collect())
Expr::ArgTuple(*s, xs.iter().map(Self::blank_unseen).collect())
}
Expr::App(s, f, xs) => Expr::App(
*s,
Self::blank_unseen(f),
xs.iter().map(|x| Self::blank_unseen(x)).collect(),
xs.iter().map(Self::blank_unseen).collect(),
),
Expr::Soup(s, xs) => Expr::Soup(*s, xs.iter().map(|x| Self::blank_unseen(x)).collect()),
Expr::Soup(s, xs) => Expr::Soup(*s, xs.iter().map(Self::blank_unseen).collect()),
Expr::Operator(s, fx, p, e) => Expr::Operator(*s, *fx, *p, Self::blank_unseen(e)),
_ => fmap(&*expr.inner),
})
Expand Down
7 changes: 1 addition & 6 deletions src/core/verify/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,11 @@ pub fn verify(expr: RcExpr) -> Result<Vec<CoreError>, CoreError> {
Ok(verifier.errors)
}

#[derive(Default)]
pub struct Verifier {
errors: Vec<CoreError>,
}

impl Default for Verifier {
fn default() -> Self {
Verifier { errors: Vec::new() }
}
}

impl Verifier {
fn verify(&mut self, expr: RcExpr) -> Result<RcExpr, CoreError> {
match &*expr.inner {
Expand Down
95 changes: 78 additions & 17 deletions src/eval/memory/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,29 @@ use std::{cell::UnsafeCell, mem::size_of, ptr::NonNull};
use std::{ptr::write, slice::from_raw_parts_mut};

use super::alloc::MutatorScope;
use super::lob::LargeObjectBlock;
use super::{
alloc::{AllocHeader, Allocator},
bump::{self, AllocError, BumpBlock},
};

#[derive(Debug)]
pub struct HeapStats {
/// Number of standard blocks allocated
pub blocks_allocated: usize,
/// Number of large objects allocated
pub lobs_allocated: usize,
}

/// Object size class.
/// - Small objects fit inside a line
/// - Medium objects span more than one line
/// - Large objects span multiple blocks
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum SizeClass {
/// Small objects fit inside a line
Small,
/// Medium objects span lines inside a block
Medium,
/// Large objects are larger than a normal block
Large,
}

Expand All @@ -41,6 +50,8 @@ pub struct HeapState {
overflow: Option<BumpBlock>,
/// Part used - not yet reclaimed
rest: Vec<BumpBlock>,
/// Large object blocks - each contains single object
lobs: Vec<LargeObjectBlock>,
}

impl Default for HeapState {
Expand All @@ -55,6 +66,7 @@ impl HeapState {
head: None,
overflow: None,
rest: vec![],
lobs: vec![],
}
}

Expand Down Expand Up @@ -91,8 +103,26 @@ impl HeapState {
});
self.overflow.as_mut().unwrap()
}

/// Create and return a new large object block able to store data
/// of the specified size
pub fn lob(&mut self, size: usize) -> &mut LargeObjectBlock {
self.lobs.push(LargeObjectBlock::new(size));
self.lobs.last_mut().unwrap()
}

/// Statistics
pub fn stats(&self) -> HeapStats {
HeapStats {
blocks_allocated: self.rest.len()
+ self.head.iter().count()
+ self.overflow.iter().count(),
lobs_allocated: self.lobs.len(),
}
}
}

/// A heap (with interior mutability)
pub struct Heap {
state: UnsafeCell<HeapState>,
}
Expand All @@ -111,6 +141,10 @@ impl Heap {
state: UnsafeCell::new(HeapState::new()),
}
}

pub fn stats(&self) -> HeapStats {
unsafe { (*self.state.get()).stats() }
}
}

impl Allocator for Heap {
Expand Down Expand Up @@ -178,26 +212,23 @@ impl Allocator for Heap {
impl Heap {
/// Allocate space
fn find_space(&self, size_bytes: usize) -> Result<*const u8, AllocError> {
let class = SizeClass::for_size(size_bytes);

if class == SizeClass::Large {
return Err(AllocError::BadRequest); // can't do it yet
}

let heap_state = unsafe { &mut *self.state.get() };

let head = heap_state.head();

let space = if class == SizeClass::Medium && size_bytes > head.current_hole_size() {
heap_state
let space = match SizeClass::for_size(size_bytes) {
SizeClass::Large => {
let lob = heap_state.lob(size_bytes);
lob.space()
}
SizeClass::Medium if size_bytes > head.current_hole_size() => heap_state
.overflow()
.bump(size_bytes)
.or_else(|| heap_state.replace_overflow().bump(size_bytes))
.expect("aargh")
} else {
head.bump(size_bytes)
.expect("aargh"),
_ => head
.bump(size_bytes)
.or_else(|| heap_state.replace_head().bump(size_bytes))
.expect("aarrgh")
.expect("aarrgh"),
};

Ok(space)
Expand All @@ -218,7 +249,15 @@ impl Heap {

#[cfg(test)]
pub mod tests {
use crate::eval::memory::syntax::Ref;
use std::iter::repeat_with;

use crate::{
common::sourcemap::Smid,
eval::memory::{
mutator::MutatorHeapView,
syntax::{LambdaForm, Ref, StgBuilder},
},
};

use super::*;

Expand All @@ -241,4 +280,26 @@ pub mod tests {
unsafe { assert_eq!(*ptr.as_ref(), Ref::num(i)) };
}
}

#[test]
pub fn test_large_object_block() {
let heap = Heap::new();
let view = MutatorHeapView::new(&heap);

let ids = repeat_with(|| -> LambdaForm {
LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default())
})
.take(32000)
.collect::<Vec<_>>();
let idarray = view.array(ids.as_slice());

view.let_(
idarray,
view.app(Ref::L(0), view.singleton(view.sym_ref("foo").unwrap()))
.unwrap(),
)
.unwrap();

assert_eq!(heap.stats().lobs_allocated, 1);
}
}
Loading

0 comments on commit 51729d0

Please sign in to comment.