Skip to content

Commit 9b32412

Browse files
Implement the BucketQueueView (#2449)
* Implement the BucketQueueView which takes the size as a template parameter. * Put random tests and unit tests. * Put benchmark and in particular BucketQueueView vs QueueView. Co-authored-by: Andreas Fackler <[email protected]>
1 parent 4fa4111 commit 9b32412

File tree

7 files changed

+1109
-4
lines changed

7 files changed

+1109
-4
lines changed

linera-views/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,7 @@ harness = false
8484
[[bench]]
8585
name = "stores"
8686
harness = false
87+
88+
[[bench]]
89+
name = "queue_view"
90+
harness = false

linera-views/benches/queue_view.rs

+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use std::{
5+
fmt::Debug,
6+
time::{Duration, Instant},
7+
};
8+
9+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
10+
#[cfg(with_dynamodb)]
11+
use linera_views::dynamo_db::create_dynamo_db_test_store;
12+
#[cfg(with_rocksdb)]
13+
use linera_views::rocks_db::create_rocks_db_test_store;
14+
#[cfg(with_scylladb)]
15+
use linera_views::scylla_db::create_scylla_db_test_store;
16+
use linera_views::{
17+
backends::memory::create_test_memory_store,
18+
bucket_queue_view::BucketQueueView,
19+
common::KeyValueStore,
20+
context::ViewContext,
21+
queue_view::QueueView,
22+
test_utils::{make_deterministic_rng, DeterministicRng},
23+
views::{CryptoHashRootView, RootView, View},
24+
};
25+
use rand::Rng;
26+
use tokio::runtime::Runtime;
27+
28+
/// The number of operations
29+
const N_OPERATIONS: usize = 1000;
30+
31+
enum Operations {
32+
Save,
33+
DeleteFront,
34+
PushBack(u8),
35+
}
36+
37+
fn generate_test_case(n_operation: usize, rng: &mut DeterministicRng) -> Vec<Operations> {
38+
let mut operations = Vec::new();
39+
let mut total_length = 0;
40+
for _ in 0..n_operation {
41+
let choice = rng.gen_range(0..10);
42+
if choice == 0 {
43+
operations.push(Operations::Save);
44+
} else if choice < 3 && total_length > 0 {
45+
operations.push(Operations::DeleteFront);
46+
total_length -= 1;
47+
} else {
48+
let val = rng.gen::<u8>();
49+
operations.push(Operations::PushBack(val));
50+
total_length += 1;
51+
}
52+
}
53+
operations
54+
}
55+
56+
#[derive(CryptoHashRootView)]
57+
pub struct QueueStateView<C> {
58+
pub queue: QueueView<C, u8>,
59+
}
60+
61+
pub async fn performance_queue_view<S: KeyValueStore + Clone + Sync + 'static>(
62+
store: S,
63+
iterations: u64,
64+
) -> Duration
65+
where
66+
S::Error: Debug + Send + Sync + 'static,
67+
{
68+
let context = ViewContext::<(), S>::create_root_context(store, ())
69+
.await
70+
.unwrap();
71+
let mut total_time = Duration::ZERO;
72+
let mut rng = make_deterministic_rng();
73+
for _ in 0..iterations {
74+
let operations = generate_test_case(N_OPERATIONS, &mut rng);
75+
let mut view = QueueStateView::load(context.clone()).await.unwrap();
76+
let measurement = Instant::now();
77+
for operation in operations {
78+
match operation {
79+
Operations::Save => {
80+
view.save().await.unwrap();
81+
}
82+
Operations::DeleteFront => {
83+
view.queue.delete_front();
84+
}
85+
Operations::PushBack(val) => {
86+
view.queue.push_back(val);
87+
}
88+
}
89+
black_box(view.queue.front().await.unwrap());
90+
}
91+
view.clear();
92+
view.save().await.unwrap();
93+
total_time += measurement.elapsed();
94+
}
95+
96+
total_time
97+
}
98+
99+
fn bench_queue_view(criterion: &mut Criterion) {
100+
criterion.bench_function("memory_queue_view", |bencher| {
101+
bencher
102+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
103+
.iter_custom(|iterations| async move {
104+
let store = create_test_memory_store();
105+
performance_queue_view(store, iterations).await
106+
})
107+
});
108+
109+
#[cfg(with_rocksdb)]
110+
criterion.bench_function("rocksdb_queue_view", |bencher| {
111+
bencher
112+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
113+
.iter_custom(|iterations| async move {
114+
let store = create_rocks_db_test_store().await;
115+
performance_queue_view(store, iterations).await
116+
})
117+
});
118+
119+
#[cfg(with_dynamodb)]
120+
criterion.bench_function("dynamodb_queue_view", |bencher| {
121+
bencher
122+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
123+
.iter_custom(|iterations| async move {
124+
let store = create_dynamo_db_test_store().await;
125+
performance_queue_view(store, iterations).await
126+
})
127+
});
128+
129+
#[cfg(with_scylladb)]
130+
criterion.bench_function("scylladb_queue_view", |bencher| {
131+
bencher
132+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
133+
.iter_custom(|iterations| async move {
134+
let store = create_scylla_db_test_store().await;
135+
performance_queue_view(store, iterations).await
136+
})
137+
});
138+
}
139+
140+
#[derive(CryptoHashRootView)]
141+
pub struct BucketQueueStateView<C> {
142+
pub queue: BucketQueueView<C, u8, 100>,
143+
}
144+
145+
pub async fn performance_bucket_queue_view<S: KeyValueStore + Clone + Sync + 'static>(
146+
store: S,
147+
iterations: u64,
148+
) -> Duration
149+
where
150+
S::Error: Debug + Send + Sync + 'static,
151+
{
152+
let context = ViewContext::<(), S>::create_root_context(store, ())
153+
.await
154+
.unwrap();
155+
let mut total_time = Duration::ZERO;
156+
let mut rng = make_deterministic_rng();
157+
for _ in 0..iterations {
158+
let operations = generate_test_case(N_OPERATIONS, &mut rng);
159+
let mut view = BucketQueueStateView::load(context.clone()).await.unwrap();
160+
//
161+
let measurement = Instant::now();
162+
for operation in operations {
163+
match operation {
164+
Operations::Save => {
165+
view.save().await.unwrap();
166+
}
167+
Operations::DeleteFront => {
168+
view.queue.delete_front().await.unwrap();
169+
}
170+
Operations::PushBack(val) => {
171+
view.queue.push_back(val);
172+
}
173+
}
174+
black_box(view.queue.front());
175+
}
176+
view.clear();
177+
view.save().await.unwrap();
178+
total_time += measurement.elapsed();
179+
}
180+
181+
total_time
182+
}
183+
184+
fn bench_bucket_queue_view(criterion: &mut Criterion) {
185+
criterion.bench_function("memory_bucket_queue_view", |bencher| {
186+
bencher
187+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
188+
.iter_custom(|iterations| async move {
189+
let store = create_test_memory_store();
190+
performance_bucket_queue_view(store, iterations).await
191+
})
192+
});
193+
194+
#[cfg(with_rocksdb)]
195+
criterion.bench_function("rocksdb_bucket_queue_view", |bencher| {
196+
bencher
197+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
198+
.iter_custom(|iterations| async move {
199+
let store = create_rocks_db_test_store().await;
200+
performance_bucket_queue_view(store, iterations).await
201+
})
202+
});
203+
204+
#[cfg(with_dynamodb)]
205+
criterion.bench_function("dynamodb_bucket_queue_view", |bencher| {
206+
bencher
207+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
208+
.iter_custom(|iterations| async move {
209+
let store = create_dynamo_db_test_store().await;
210+
performance_bucket_queue_view(store, iterations).await
211+
})
212+
});
213+
214+
#[cfg(with_scylladb)]
215+
criterion.bench_function("scylladb_bucket_queue_view", |bencher| {
216+
bencher
217+
.to_async(Runtime::new().expect("Failed to create Tokio runtime"))
218+
.iter_custom(|iterations| async move {
219+
let store = create_scylla_db_test_store().await;
220+
performance_bucket_queue_view(store, iterations).await
221+
})
222+
});
223+
}
224+
225+
criterion_group!(benches, bench_queue_view, bench_bucket_queue_view);
226+
criterion_main!(benches);

linera-views/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ pub use backends::rocks_db;
9797
pub use backends::scylla_db;
9898
pub use backends::{journaling, lru_caching, memory, value_splitting};
9999
pub use views::{
100-
collection_view, hashable_wrapper, key_value_store_view, log_view, map_view, queue_view,
101-
reentrant_collection_view, register_view, set_view,
100+
bucket_queue_view, collection_view, hashable_wrapper, key_value_store_view, log_view, map_view,
101+
queue_view, reentrant_collection_view, register_view, set_view,
102102
};
103103
/// Re-exports used by the derive macros of this library.
104104
#[doc(hidden)]

0 commit comments

Comments
 (0)