Skip to content

Commit cb11124

Browse files
authored
Merge pull request #28 from fjall-rs/perf/disjoint
Optimize reading over disjoint levels
2 parents dc6042f + caae78e commit cb11124

File tree

22 files changed

+679
-236
lines changed

22 files changed

+679
-236
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "lsm-tree"
33
description = "A K.I.S.S. implementation of log-structured merge trees (LSM-trees/LSMTs)"
44
license = "MIT OR Apache-2.0"
5-
version = "0.6.5"
5+
version = "0.7.0"
66
edition = "2021"
77
rust-version = "1.74.0"
88
readme = "README.md"

benches/lsmt.rs

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,29 @@
11
use criterion::{criterion_group, criterion_main, Criterion};
2-
use lsm_tree::{bloom::BloomFilter, segment::block::ValueBlock, BlockCache, Config, Value};
2+
use lsm_tree::{
3+
bloom::BloomFilter, segment::block::ValueBlock, BlockCache, Config, MemTable, Value,
4+
};
5+
use nanoid::nanoid;
36
use std::{io::Write, sync::Arc};
47

8+
fn memtable_get_upper_bound(c: &mut Criterion) {
9+
let memtable = MemTable::default();
10+
11+
for _ in 0..1_000_000 {
12+
memtable.insert(Value {
13+
key: format!("abc_{}", nanoid!()).as_bytes().into(),
14+
value: vec![].into(),
15+
seqno: 0,
16+
value_type: lsm_tree::ValueType::Value,
17+
});
18+
}
19+
20+
c.bench_function("memtable get", |b| {
21+
b.iter(|| {
22+
memtable.get("abc", None);
23+
});
24+
});
25+
}
26+
527
fn value_block_size(c: &mut Criterion) {
628
let mut group = c.benchmark_group("ValueBlock::size");
729

@@ -137,56 +159,104 @@ fn bloom_filter_contains(c: &mut Criterion) {
137159
});
138160
}
139161

162+
// TODO: benchmark .prefix().next() and .next_back(), disjoint and non-disjoint
163+
140164
fn tree_get_pairs(c: &mut Criterion) {
141165
let mut group = c.benchmark_group("Get pairs");
166+
group.sample_size(10);
167+
168+
for segment_count in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] {
169+
{
170+
let folder = tempfile::tempdir().unwrap();
171+
let tree = Config::new(folder)
172+
.block_size(1_024)
173+
.block_cache(Arc::new(BlockCache::with_capacity_bytes(0)))
174+
.open()
175+
.unwrap();
176+
177+
let mut x = 0_u64;
178+
179+
for _ in 0..segment_count {
180+
for _ in 0..10 {
181+
let key = x.to_be_bytes();
182+
x += 1;
183+
tree.insert(key, key, 0);
184+
}
185+
tree.flush_active_memtable().unwrap();
186+
}
142187

143-
for segment_count in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1_024] {
144-
let folder = tempfile::tempdir().unwrap();
145-
let tree = Config::new(folder)
146-
.block_size(1_024)
147-
.block_cache(Arc::new(BlockCache::with_capacity_bytes(0)))
148-
.open()
149-
.unwrap();
150-
151-
// TODO: disjoint
188+
group.bench_function(
189+
&format!("Tree::first_key_value (disjoint), {segment_count} segments"),
190+
|b| {
191+
b.iter(|| {
192+
assert!(tree.first_key_value().unwrap().is_some());
193+
});
194+
},
195+
);
196+
197+
group.bench_function(
198+
&format!("Tree::last_key_value (disjoint), {segment_count} segments"),
199+
|b| {
200+
b.iter(|| {
201+
assert!(tree.last_key_value().unwrap().is_some());
202+
});
203+
},
204+
);
205+
}
152206

153-
for _ in 0..segment_count {
154-
for x in 0u16..10 {
155-
let key = x.to_be_bytes();
156-
tree.insert(key, key, 0);
207+
{
208+
let folder = tempfile::tempdir().unwrap();
209+
let tree = Config::new(folder)
210+
.block_size(1_024)
211+
.block_cache(Arc::new(BlockCache::with_capacity_bytes(0)))
212+
.open()
213+
.unwrap();
214+
215+
let mut x = 0_u64;
216+
217+
for _ in 0..segment_count {
218+
for _ in 0..10 {
219+
let key = x.to_be_bytes();
220+
x += 1;
221+
tree.insert(key, key, 0);
222+
}
223+
tree.insert("a", vec![], 0);
224+
tree.insert(u64::MAX.to_be_bytes(), vec![], 0);
225+
tree.flush_active_memtable().unwrap();
157226
}
158-
tree.flush_active_memtable().unwrap();
159-
}
160227

161-
group.bench_function(
162-
&format!("Tree::first_key_value, {segment_count} segments"),
163-
|b| {
164-
b.iter(|| {
165-
assert!(tree.first_key_value().unwrap().is_some());
166-
});
167-
},
168-
);
169-
170-
group.bench_function(
171-
&format!("Tree::last_key_value, {segment_count} segments"),
172-
|b| {
173-
b.iter(|| {
174-
assert!(tree.last_key_value().unwrap().is_some());
175-
});
176-
},
177-
);
228+
group.bench_function(
229+
&format!("Tree::first_key_value (non-disjoint), {segment_count} segments"),
230+
|b| {
231+
b.iter(|| {
232+
assert!(tree.first_key_value().unwrap().is_some());
233+
});
234+
},
235+
);
236+
237+
group.bench_function(
238+
&format!("Tree::last_key_value (non-disjoint), {segment_count} segments"),
239+
|b| {
240+
b.iter(|| {
241+
assert!(tree.last_key_value().unwrap().is_some());
242+
});
243+
},
244+
);
245+
}
178246
}
179247
}
180248

181249
// TODO: benchmark point read disjoint vs non-disjoint level
182250

183251
criterion_group!(
184252
benches,
253+
memtable_get_upper_bound,
185254
value_block_size,
186255
load_block_from_disk,
187256
file_descriptor,
188257
bloom_filter_construction,
189258
bloom_filter_contains,
190259
tree_get_pairs,
260+
// first_kv_disjoint
191261
);
192262
criterion_main!(benches);

src/compaction/fifo.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{Choice, CompactionStrategy};
2-
use crate::{config::PersistedConfig, levels::Levels, time::unix_timestamp};
2+
use crate::{config::PersistedConfig, levels::LevelManifest, time::unix_timestamp};
33
use std::ops::Deref;
44

55
// TODO: L0 stall/halt thresholds should be configurable
@@ -41,7 +41,7 @@ impl Strategy {
4141
}
4242

4343
impl CompactionStrategy for Strategy {
44-
fn choose(&self, levels: &Levels, config: &PersistedConfig) -> Choice {
44+
fn choose(&self, levels: &LevelManifest, config: &PersistedConfig) -> Choice {
4545
let resolved_view = levels.resolved_view();
4646

4747
let mut first_level = resolved_view
@@ -74,8 +74,9 @@ impl CompactionStrategy for Strategy {
7474
if db_size > self.limit {
7575
let mut bytes_to_delete = db_size - self.limit;
7676

77-
// Sort the level by oldest to newest
78-
first_level.sort_by(|a, b| a.metadata.seqnos.0.cmp(&b.metadata.seqnos.0));
77+
// NOTE: Sort the level by oldest to newest (levels are sorted from newest to oldest)
78+
// so we can just reverse
79+
first_level.reverse();
7980

8081
for segment in first_level {
8182
if bytes_to_delete == 0 {
@@ -106,7 +107,7 @@ mod tests {
106107
descriptor_table::FileDescriptorTable,
107108
file::LEVELS_MANIFEST_FILE,
108109
key_range::KeyRange,
109-
levels::Levels,
110+
levels::LevelManifest,
110111
segment::{block_index::BlockIndex, meta::Metadata, Segment},
111112
time::unix_timestamp,
112113
};
@@ -136,7 +137,7 @@ mod tests {
136137
key_range: KeyRange::new((vec![].into(), vec![].into())),
137138
tombstone_count: 0,
138139
uncompressed_size: 0,
139-
seqnos: (0, 0),
140+
seqnos: (0, created_at as u64),
140141
},
141142
block_cache,
142143

@@ -150,7 +151,7 @@ mod tests {
150151
let tempdir = tempfile::tempdir()?;
151152
let compactor = Strategy::new(u64::MAX, Some(5_000));
152153

153-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
154+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
154155

155156
levels.add(fixture_segment("1".into(), 1));
156157
levels.add(fixture_segment("2".into(), unix_timestamp().as_micros()));
@@ -168,7 +169,7 @@ mod tests {
168169
let tempdir = tempfile::tempdir()?;
169170
let compactor = Strategy::new(1, None);
170171

171-
let levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
172+
let levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
172173

173174
assert_eq!(
174175
compactor.choose(&levels, &PersistedConfig::default()),
@@ -183,7 +184,7 @@ mod tests {
183184
let tempdir = tempfile::tempdir()?;
184185
let compactor = Strategy::new(4, None);
185186

186-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
187+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
187188

188189
levels.add(fixture_segment("1".into(), 1));
189190
assert_eq!(
@@ -217,7 +218,7 @@ mod tests {
217218
let tempdir = tempfile::tempdir()?;
218219
let compactor = Strategy::new(2, None);
219220

220-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
221+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
221222
levels.add(fixture_segment("1".into(), 1));
222223
levels.add(fixture_segment("2".into(), 2));
223224
levels.add(fixture_segment("3".into(), 3));

src/compaction/levelled.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use super::{Choice, CompactionStrategy, Input as CompactionInput};
2-
use crate::{config::PersistedConfig, key_range::KeyRange, levels::Levels, segment::Segment};
2+
use crate::{
3+
config::PersistedConfig, key_range::KeyRange, levels::LevelManifest, segment::Segment,
4+
};
35
use std::{ops::Deref, sync::Arc};
46

57
/// Levelled compaction strategy (LCS)
@@ -67,7 +69,7 @@ fn desired_level_size_in_bytes(level_idx: u8, ratio: u8, target_size: u32) -> us
6769
// TODO: instead of rewriting
6870

6971
impl CompactionStrategy for Strategy {
70-
fn choose(&self, levels: &Levels, config: &PersistedConfig) -> Choice {
72+
fn choose(&self, levels: &LevelManifest, config: &PersistedConfig) -> Choice {
7173
let resolved_view = levels.resolved_view();
7274

7375
// If there are any levels that already have a compactor working on it
@@ -111,8 +113,8 @@ impl CompactionStrategy for Strategy {
111113
if overshoot > 0 {
112114
let mut segments_to_compact = vec![];
113115

114-
let mut level = level.deref().clone();
115-
level.sort_by(|a, b| a.metadata.key_range.0.cmp(&b.metadata.key_range.0));
116+
let mut level = level.clone();
117+
level.sort_by_key_range();
116118

117119
for segment in level.iter().take(config.level_ratio.into()).cloned() {
118120
if overshoot == 0 {
@@ -195,7 +197,7 @@ mod tests {
195197
descriptor_table::FileDescriptorTable,
196198
file::LEVELS_MANIFEST_FILE,
197199
key_range::KeyRange,
198-
levels::Levels,
200+
levels::LevelManifest,
199201
segment::{block_index::BlockIndex, meta::Metadata, Segment},
200202
time::unix_timestamp,
201203
Config,
@@ -247,7 +249,7 @@ mod tests {
247249
..Default::default()
248250
};
249251

250-
let levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
252+
let levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
251253

252254
assert_eq!(
253255
compactor.choose(&levels, &Config::default().inner),
@@ -265,7 +267,7 @@ mod tests {
265267
..Default::default()
266268
};
267269

268-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
270+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
269271

270272
levels.add(fixture_segment(
271273
"1".into(),
@@ -329,7 +331,7 @@ mod tests {
329331
..Default::default()
330332
};
331333

332-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
334+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
333335
levels.add(fixture_segment(
334336
"1".into(),
335337
string_key_range("h", "t"),
@@ -388,7 +390,7 @@ mod tests {
388390
..Default::default()
389391
};
390392

391-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
393+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
392394
levels.add(fixture_segment(
393395
"1".into(),
394396
string_key_range("a", "g"),
@@ -461,7 +463,7 @@ mod tests {
461463
};
462464
let config = Config::default().level_ratio(2);
463465

464-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
466+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
465467

466468
levels.insert_into_level(
467469
2,
@@ -506,7 +508,7 @@ mod tests {
506508
};
507509
let config = Config::default().level_ratio(2);
508510

509-
let mut levels = Levels::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
511+
let mut levels = LevelManifest::create_new(4, tempdir.path().join(LEVELS_MANIFEST_FILE))?;
510512

511513
levels.insert_into_level(
512514
3,

0 commit comments

Comments
 (0)