Skip to content

Commit 51729d0

Browse files
authored
Fix large object allocation (#175)
1 parent 1c2b4a8 commit 51729d0

File tree

12 files changed

+367
-237
lines changed

12 files changed

+367
-237
lines changed

Cargo.lock

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

Cargo.toml

+15-15
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,39 @@ authors = ["gmorpheme <[email protected]>"]
55
edition = "2018"
66

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

1010
[dev-dependencies]
11-
criterion = "0.3"
11+
criterion = "0.3.5"
1212

1313
[dependencies]
14-
regex = "1.4.5"
15-
matches = "0.1.8"
14+
regex = "1.5.4"
15+
matches = "0.1.9"
1616
codespan = "0.11.1"
1717
codespan-reporting = "0.11.1"
18-
lalrpop-util = "0.19.5"
19-
serde_json = "1.0.64"
18+
lalrpop-util = "0.19.6"
19+
serde_json = "1.0.72"
2020
yaml-rust = { git = "https://github.com/curvelogic/yaml-rust", rev = "e3a9b432c43fcf9a19f1836657091caf7ae3b15f" }
2121
unic-ucd-category = "0.9.0"
22-
itertools = "0.10.0"
23-
thiserror = "1.0.24"
24-
structopt = "0.3.21"
25-
url = "2.2.1"
22+
itertools = "0.10.1"
23+
thiserror = "1.0.30"
24+
structopt = "0.3.25"
25+
url = "2.2.2"
2626
pretty = "0.10.0"
2727
moniker = { git = "https://github.com/curvelogic/moniker", rev = "c620b2a69d6cad4724cba07775ed65a7ae556c9c" }
28-
indexmap = "1.6.2"
29-
petgraph = "0.5.1"
28+
indexmap = "1.7.0"
29+
petgraph = "0.6.0"
3030
atty = "0.2"
3131
csv = "1.1.6"
3232
lazy_static = "1.4.0"
3333
chrono = "0.4.19"
34-
bitflags = "1.2.1"
34+
bitflags = "1.3.2"
3535
quick-xml = "0.22.0"
36-
chrono-tz = "0.5.3"
36+
chrono-tz = "0.6.0"
3737
toml = { version = "0.5.8", features = ["preserve_order"] }
3838
dirs = "3.0.2"
3939
html5ever = "0.25.1"
40-
lru = "0.6.5"
40+
lru = "0.7.0"
4141
uuid = { version = "0.8.2", features = ["serde", "v4"] }
4242
webbrowser = "0.5.5"
4343
bitmaps = "3.1.0"

src/common/sourcemap.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,11 @@ pub struct SourceInfo {
110110
}
111111

112112
/// Store all source info...
113+
#[derive(Default)]
113114
pub struct SourceMap {
114115
source: Vec<SourceInfo>,
115116
}
116117

117-
impl Default for SourceMap {
118-
fn default() -> Self {
119-
Self { source: vec![] }
120-
}
121-
}
122-
123118
impl SourceMap {
124119
/// Create a new, empty database of files.
125120
pub fn new() -> Self {

src/core/cook/fixity.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,11 @@ type Env = SimpleEnvironment<FreeVar<String>, OpMeta>;
2121

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

28-
impl Default for Distributor {
29-
fn default() -> Self {
30-
Distributor {
31-
env: Env::default(),
32-
}
33-
}
34-
}
35-
3629
impl Distributor {
3730
/// If an expression defines an operator, strip off (and return)
3831
/// the operator metadata and expose the operator callable.

src/core/cook/mod.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub fn cook(expr: RcExpr) -> Result<RcExpr, CoreError> {
2020
///
2121
/// Needs to track whether we are within the scope of an
2222
/// expression-anaophoric lambda.
23+
#[derive(Default)]
2324
pub struct Cooker {
2425
/// True when we have traversed into the scope of expression anaphora
2526
in_expr_anaphor_scope: bool,
@@ -31,16 +32,6 @@ pub struct Cooker {
3132
pending_block_anaphora: HashMap<Anaphor<Smid, i32>, FreeVar<String>>,
3233
}
3334

34-
impl Default for Cooker {
35-
fn default() -> Self {
36-
Cooker {
37-
in_expr_anaphor_scope: false,
38-
pending_expr_anaphora: HashMap::new(),
39-
pending_block_anaphora: HashMap::new(),
40-
}
41-
}
42-
}
43-
4435
impl Cooker {
4536
/// Cook the expression `expr` resolving all operators and
4637
/// eliminating Expr::Soup in favour of hierarchical expressions.

src/core/inline/reduce.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::core::expr::*;
44
use crate::core::transform::succ;
55
use moniker::*;
66

7+
#[allow(clippy::redundant_closure)]
78
pub fn inline_pass(expr: &RcExpr) -> Result<RcExpr, CoreError> {
89
distribute(expr).and_then(|ref e| beta_reduce(e))
910
}
@@ -75,10 +76,10 @@ fn beta_reduce(expr: &RcExpr) -> Result<RcExpr, CoreError> {
7576
// args for now
7677
expr.walk_safe(&mut |e| beta_reduce(&e))
7778
} else {
78-
let args =
79-
xs.iter()
80-
.map(|arg| beta_reduce(arg))
81-
.collect::<Result<Vec<RcExpr>, CoreError>>()?;
79+
let args = xs
80+
.iter()
81+
.map(beta_reduce)
82+
.collect::<Result<Vec<RcExpr>, CoreError>>()?;
8283

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

src/core/simplify/prune.rs

+6-16
Original file line numberDiff line numberDiff line change
@@ -116,23 +116,13 @@ impl RcMarkExpr {
116116
}
117117
}
118118

119-
#[derive(Debug)]
119+
#[derive(Debug, Default)]
120120
pub struct ScopeTracker<'expr> {
121121
scopes: VecDeque<&'expr RcMarkExpr>,
122122
reachable: bool,
123123
marked_count: usize,
124124
}
125125

126-
impl<'expr> Default for ScopeTracker<'expr> {
127-
fn default() -> Self {
128-
ScopeTracker {
129-
scopes: VecDeque::new(),
130-
reachable: false,
131-
marked_count: 0,
132-
}
133-
}
134-
}
135-
136126
impl<'expr> ScopeTracker<'expr> {
137127
pub fn mark_reachable(&mut self, expr: &'expr RcMarkExpr) {
138128
Self::mark_body(expr);
@@ -320,9 +310,9 @@ impl<'expr> ScopeTracker<'expr> {
320310
*s,
321311
Self::blank_unseen(e),
322312
n.clone(),
323-
fb.as_ref().map(|x| Self::blank_unseen(x)),
313+
fb.as_ref().map(Self::blank_unseen),
324314
),
325-
Expr::List(s, xs) => Expr::List(*s, xs.iter().map(|x| Self::blank_unseen(x)).collect()),
315+
Expr::List(s, xs) => Expr::List(*s, xs.iter().map(Self::blank_unseen).collect()),
326316
Expr::Block(s, block_map) => Expr::Block(
327317
*s,
328318
block_map
@@ -332,14 +322,14 @@ impl<'expr> ScopeTracker<'expr> {
332322
),
333323
Expr::Meta(s, e, m) => Expr::Meta(*s, Self::blank_unseen(e), Self::blank_unseen(m)),
334324
Expr::ArgTuple(s, xs) => {
335-
Expr::ArgTuple(*s, xs.iter().map(|x| Self::blank_unseen(x)).collect())
325+
Expr::ArgTuple(*s, xs.iter().map(Self::blank_unseen).collect())
336326
}
337327
Expr::App(s, f, xs) => Expr::App(
338328
*s,
339329
Self::blank_unseen(f),
340-
xs.iter().map(|x| Self::blank_unseen(x)).collect(),
330+
xs.iter().map(Self::blank_unseen).collect(),
341331
),
342-
Expr::Soup(s, xs) => Expr::Soup(*s, xs.iter().map(|x| Self::blank_unseen(x)).collect()),
332+
Expr::Soup(s, xs) => Expr::Soup(*s, xs.iter().map(Self::blank_unseen).collect()),
343333
Expr::Operator(s, fx, p, e) => Expr::Operator(*s, *fx, *p, Self::blank_unseen(e)),
344334
_ => fmap(&*expr.inner),
345335
})

src/core/verify/content.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,11 @@ pub fn verify(expr: RcExpr) -> Result<Vec<CoreError>, CoreError> {
99
Ok(verifier.errors)
1010
}
1111

12+
#[derive(Default)]
1213
pub struct Verifier {
1314
errors: Vec<CoreError>,
1415
}
1516

16-
impl Default for Verifier {
17-
fn default() -> Self {
18-
Verifier { errors: Vec::new() }
19-
}
20-
}
21-
2217
impl Verifier {
2318
fn verify(&mut self, expr: RcExpr) -> Result<RcExpr, CoreError> {
2419
match &*expr.inner {

src/eval/memory/heap.rs

+78-17
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,29 @@ use std::{cell::UnsafeCell, mem::size_of, ptr::NonNull};
44
use std::{ptr::write, slice::from_raw_parts_mut};
55

66
use super::alloc::MutatorScope;
7+
use super::lob::LargeObjectBlock;
78
use super::{
89
alloc::{AllocHeader, Allocator},
910
bump::{self, AllocError, BumpBlock},
1011
};
1112

13+
#[derive(Debug)]
14+
pub struct HeapStats {
15+
/// Number of standard blocks allocated
16+
pub blocks_allocated: usize,
17+
/// Number of large objects allocated
18+
pub lobs_allocated: usize,
19+
}
20+
1221
/// Object size class.
13-
/// - Small objects fit inside a line
14-
/// - Medium objects span more than one line
15-
/// - Large objects span multiple blocks
1622
#[repr(u8)]
1723
#[derive(Copy, Clone, Debug, PartialEq)]
1824
pub enum SizeClass {
25+
/// Small objects fit inside a line
1926
Small,
27+
/// Medium objects span lines inside a block
2028
Medium,
29+
/// Large objects are larger than a normal block
2130
Large,
2231
}
2332

@@ -41,6 +50,8 @@ pub struct HeapState {
4150
overflow: Option<BumpBlock>,
4251
/// Part used - not yet reclaimed
4352
rest: Vec<BumpBlock>,
53+
/// Large object blocks - each contains single object
54+
lobs: Vec<LargeObjectBlock>,
4455
}
4556

4657
impl Default for HeapState {
@@ -55,6 +66,7 @@ impl HeapState {
5566
head: None,
5667
overflow: None,
5768
rest: vec![],
69+
lobs: vec![],
5870
}
5971
}
6072

@@ -91,8 +103,26 @@ impl HeapState {
91103
});
92104
self.overflow.as_mut().unwrap()
93105
}
106+
107+
/// Create and return a new large object block able to store data
108+
/// of the specified size
109+
pub fn lob(&mut self, size: usize) -> &mut LargeObjectBlock {
110+
self.lobs.push(LargeObjectBlock::new(size));
111+
self.lobs.last_mut().unwrap()
112+
}
113+
114+
/// Statistics
115+
pub fn stats(&self) -> HeapStats {
116+
HeapStats {
117+
blocks_allocated: self.rest.len()
118+
+ self.head.iter().count()
119+
+ self.overflow.iter().count(),
120+
lobs_allocated: self.lobs.len(),
121+
}
122+
}
94123
}
95124

125+
/// A heap (with interior mutability)
96126
pub struct Heap {
97127
state: UnsafeCell<HeapState>,
98128
}
@@ -111,6 +141,10 @@ impl Heap {
111141
state: UnsafeCell::new(HeapState::new()),
112142
}
113143
}
144+
145+
pub fn stats(&self) -> HeapStats {
146+
unsafe { (*self.state.get()).stats() }
147+
}
114148
}
115149

116150
impl Allocator for Heap {
@@ -178,26 +212,23 @@ impl Allocator for Heap {
178212
impl Heap {
179213
/// Allocate space
180214
fn find_space(&self, size_bytes: usize) -> Result<*const u8, AllocError> {
181-
let class = SizeClass::for_size(size_bytes);
182-
183-
if class == SizeClass::Large {
184-
return Err(AllocError::BadRequest); // can't do it yet
185-
}
186-
187215
let heap_state = unsafe { &mut *self.state.get() };
188-
189216
let head = heap_state.head();
190217

191-
let space = if class == SizeClass::Medium && size_bytes > head.current_hole_size() {
192-
heap_state
218+
let space = match SizeClass::for_size(size_bytes) {
219+
SizeClass::Large => {
220+
let lob = heap_state.lob(size_bytes);
221+
lob.space()
222+
}
223+
SizeClass::Medium if size_bytes > head.current_hole_size() => heap_state
193224
.overflow()
194225
.bump(size_bytes)
195226
.or_else(|| heap_state.replace_overflow().bump(size_bytes))
196-
.expect("aargh")
197-
} else {
198-
head.bump(size_bytes)
227+
.expect("aargh"),
228+
_ => head
229+
.bump(size_bytes)
199230
.or_else(|| heap_state.replace_head().bump(size_bytes))
200-
.expect("aarrgh")
231+
.expect("aarrgh"),
201232
};
202233

203234
Ok(space)
@@ -218,7 +249,15 @@ impl Heap {
218249

219250
#[cfg(test)]
220251
pub mod tests {
221-
use crate::eval::memory::syntax::Ref;
252+
use std::iter::repeat_with;
253+
254+
use crate::{
255+
common::sourcemap::Smid,
256+
eval::memory::{
257+
mutator::MutatorHeapView,
258+
syntax::{LambdaForm, Ref, StgBuilder},
259+
},
260+
};
222261

223262
use super::*;
224263

@@ -241,4 +280,26 @@ pub mod tests {
241280
unsafe { assert_eq!(*ptr.as_ref(), Ref::num(i)) };
242281
}
243282
}
283+
284+
#[test]
285+
pub fn test_large_object_block() {
286+
let heap = Heap::new();
287+
let view = MutatorHeapView::new(&heap);
288+
289+
let ids = repeat_with(|| -> LambdaForm {
290+
LambdaForm::new(1, view.atom(Ref::L(0)).unwrap().as_ptr(), Smid::default())
291+
})
292+
.take(32000)
293+
.collect::<Vec<_>>();
294+
let idarray = view.array(ids.as_slice());
295+
296+
view.let_(
297+
idarray,
298+
view.app(Ref::L(0), view.singleton(view.sym_ref("foo").unwrap()))
299+
.unwrap(),
300+
)
301+
.unwrap();
302+
303+
assert_eq!(heap.stats().lobs_allocated, 1);
304+
}
244305
}

0 commit comments

Comments
 (0)