Skip to content

Commit 233c63c

Browse files
refactor(storage-proofs): move cache into global hash map
This allows the cache to be reused accross multiple graphs
1 parent 1f46935 commit 233c63c

File tree

1 file changed

+111
-92
lines changed

1 file changed

+111
-92
lines changed

storage-proofs/src/zigzag_graph.rs

Lines changed: 111 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashMap;
12
use std::marker::PhantomData;
23
use std::sync::{Arc, RwLock};
34

@@ -11,75 +12,73 @@ use crate::settings;
1112
/// The expansion degree used for ZigZag Graphs.
1213
pub const EXP_DEGREE: usize = 8;
1314

14-
// Cache of node's parents.
15-
pub type ParentCache = Vec<Option<Vec<u32>>>;
15+
lazy_static! {
16+
// This parents cache is currently used for the *expanded parents only*, generated
17+
// by the expensive Feistel operations in the ZigZag, it doesn't contain the
18+
// "base" (in the `Graph` terminology) parents, which are cheaper to compute.
19+
// It is indexed by the `Graph.identifier`, to ensure that the right cache is used.
20+
static ref PARENT_CACHE: Arc<RwLock<HashMap<String, ParentCache>>> = Arc::new(RwLock::new(HashMap::new()));
21+
}
1622

1723
// ZigZagGraph will hold two different (but related) `ParentCache`,
18-
// the first one for the `forward` direction and the second one
19-
// for the `reversed`.
24+
// the first one for the `forward` direction and the second one for the `reversed`.
2025
#[derive(Debug, Clone)]
21-
pub struct ShareableParentCache {
22-
forward: Arc<RwLock<ParentCache>>,
23-
reverse: Arc<RwLock<ParentCache>>,
26+
pub struct ParentCache {
27+
forward: Vec<Option<Vec<u32>>>,
28+
reverse: Vec<Option<Vec<u32>>>,
2429
// Keep the size of the cache outside the lock to be easily accessible.
2530
cache_entries: u32,
2631
}
2732

28-
impl ShareableParentCache {
33+
impl ParentCache {
2934
pub fn new(cache_entries: u32) -> Self {
30-
ShareableParentCache {
31-
forward: Arc::new(RwLock::new(vec![None; cache_entries as usize])),
32-
reverse: Arc::new(RwLock::new(vec![None; cache_entries as usize])),
35+
ParentCache {
36+
forward: vec![None; cache_entries as usize],
37+
reverse: vec![None; cache_entries as usize],
3338
cache_entries,
3439
}
3540
}
3641

3742
pub fn contains_forward(&self, node: u32) -> bool {
3843
assert!(node < self.cache_entries);
39-
self.forward.read().unwrap()[node as usize].is_some()
44+
self.forward[node as usize].is_some()
4045
}
4146

4247
pub fn contains_reverse(&self, node: u32) -> bool {
4348
assert!(node < self.cache_entries);
44-
self.reverse.read().unwrap()[node as usize].is_some()
49+
self.reverse[node as usize].is_some()
4550
}
4651

4752
pub fn read_forward<F, T>(&self, node: u32, mut cb: F) -> T
4853
where
4954
F: FnMut(Option<&Vec<u32>>) -> T,
5055
{
5156
assert!(node < self.cache_entries);
52-
let lock = self.forward.read().unwrap();
53-
cb(lock[node as usize].as_ref())
57+
cb(self.forward[node as usize].as_ref())
5458
}
5559

5660
pub fn read_reverse<F, T>(&self, node: u32, mut cb: F) -> T
5761
where
5862
F: FnMut(Option<&Vec<u32>>) -> T,
5963
{
6064
assert!(node < self.cache_entries);
61-
let lock = self.reverse.read().unwrap();
62-
cb(lock[node as usize].as_ref())
65+
cb(self.reverse[node as usize].as_ref())
6366
}
6467

65-
pub fn write_forward(&self, node: u32, parents: Vec<u32>) {
68+
pub fn write_forward(&mut self, node: u32, parents: Vec<u32>) {
6669
assert!(node < self.cache_entries);
6770

68-
let mut write_lock = self.forward.write().unwrap();
69-
70-
let old_value = std::mem::replace(&mut write_lock[node as usize], Some(parents));
71+
let old_value = std::mem::replace(&mut self.forward[node as usize], Some(parents));
7172

7273
debug_assert_eq!(old_value, None);
7374
// We shouldn't be rewriting entries (with most likely the same values),
7475
// this would be a clear indication of a bug.
7576
}
7677

77-
pub fn write_reverse(&self, node: u32, parents: Vec<u32>) {
78+
pub fn write_reverse(&mut self, node: u32, parents: Vec<u32>) {
7879
assert!(node < self.cache_entries);
7980

80-
let mut write_lock = self.reverse.write().unwrap();
81-
82-
let old_value = std::mem::replace(&mut write_lock[node as usize], Some(parents));
81+
let old_value = std::mem::replace(&mut self.reverse[node as usize], Some(parents));
8382

8483
debug_assert_eq!(old_value, None);
8584
// We shouldn't be rewriting entries (with most likely the same values),
@@ -97,16 +96,8 @@ where
9796
base_graph: G,
9897
pub reversed: bool,
9998
feistel_precomputed: FeistelPrecomputed,
100-
101-
// This parents cache is currently used for the *expanded parents only*, generated
102-
// by the expensive Feistel operations in the ZigZag, it doesn't contain the
103-
// "base" (in the `Graph` terminology) parents, which are cheaper to compute.
104-
// This is not an LRU cache, it holds the first `cache_entries` of the total
105-
// possible `base_graph.size()` (the assumption here is that we either request
106-
// all entries sequentially when encoding or any random entry once when proving
107-
// or verifying, but there's no locality to take advantage of so keep the logic
108-
// as simple as possible).
109-
parents_cache: Option<ShareableParentCache>,
99+
id: String,
100+
use_cache: bool,
110101
_h: PhantomData<H>,
111102
}
112103

@@ -115,14 +106,14 @@ pub type ZigZagBucketGraph<H> = ZigZagGraph<H, BucketGraph<H>>;
115106
impl<'a, H, G> Layerable<H> for ZigZagGraph<H, G>
116107
where
117108
H: Hasher,
118-
G: Graph<H> + 'static,
109+
G: Graph<H> + ParameterSetMetadata + 'static,
119110
{
120111
}
121112

122113
impl<H, G> ZigZagGraph<H, G>
123114
where
124115
H: Hasher,
125-
G: Graph<H>,
116+
G: Graph<H> + ParameterSetMetadata,
126117
{
127118
pub fn new(
128119
base_graph: Option<G>,
@@ -136,25 +127,40 @@ where
136127
assert_eq!(expansion_degree, EXP_DEGREE);
137128
}
138129

139-
let parents_cache = if settings::SETTINGS.lock().unwrap().maximize_caching {
140-
info!("using parents cache of unlimited size",);
141-
assert!(nodes <= std::u32::MAX as usize);
142-
Some(ShareableParentCache::new(nodes as u32))
143-
} else {
144-
None
145-
};
130+
let use_cache = settings::SETTINGS.lock().unwrap().maximize_caching;
146131

147-
ZigZagGraph {
148-
base_graph: match base_graph {
149-
Some(graph) => graph,
150-
None => G::new(nodes, base_degree, 0, seed),
151-
},
132+
let base_graph = match base_graph {
133+
Some(graph) => graph,
134+
None => G::new(nodes, base_degree, 0, seed),
135+
};
136+
let bg_id = base_graph.identifier();
137+
138+
let res = ZigZagGraph {
139+
base_graph,
140+
id: format!(
141+
"zigzag_graph::ZigZagGraph{{expansion_degree: {} base_graph: {} }}",
142+
expansion_degree, bg_id,
143+
),
152144
expansion_degree,
145+
use_cache,
153146
reversed: false,
154147
feistel_precomputed: feistel::precompute((expansion_degree * nodes) as feistel::Index),
155-
parents_cache,
156148
_h: PhantomData,
149+
};
150+
151+
if use_cache {
152+
info!("using parents cache of unlimited size",);
153+
assert!(nodes <= std::u32::MAX as usize);
154+
155+
if !PARENT_CACHE.read().unwrap().contains_key(&res.id) {
156+
PARENT_CACHE
157+
.write()
158+
.unwrap()
159+
.insert(res.id.clone(), ParentCache::new(nodes as u32));
160+
}
157161
}
162+
163+
res
158164
}
159165
}
160166

@@ -164,11 +170,7 @@ where
164170
G: Graph<H> + ParameterSetMetadata,
165171
{
166172
fn identifier(&self) -> String {
167-
format!(
168-
"zigzag_graph::ZigZagGraph{{expansion_degree: {} base_graph: {} }}",
169-
self.expansion_degree,
170-
self.base_graph.identifier()
171-
)
173+
self.id.clone()
172174
}
173175

174176
fn sector_size(&self) -> u64 {
@@ -263,7 +265,7 @@ impl<Z: ZigZag> Graph<Z::BaseHasher> for Z {
263265
impl<'a, H, G> ZigZagGraph<H, G>
264266
where
265267
H: Hasher,
266-
G: Graph<H>,
268+
G: Graph<H> + ParameterSetMetadata,
267269
{
268270
// Assign `expansion_degree` parents to `node` using an invertible function. That
269271
// means we can't just generate random values between `[0, size())`, we need to
@@ -329,22 +331,45 @@ where
329331
// the current direction set in the graph and return a copy of it (or
330332
// `None` to signal a cache miss).
331333
fn contains_parents_cache(&self, node: usize) -> bool {
332-
if let Some(ref cache) = self.parents_cache {
333-
if self.forward() {
334-
cache.contains_forward(node as u32)
334+
if self.use_cache {
335+
if let Some(ref cache) = PARENT_CACHE.read().unwrap().get(&self.id) {
336+
if self.forward() {
337+
cache.contains_forward(node as u32)
338+
} else {
339+
cache.contains_reverse(node as u32)
340+
}
335341
} else {
336-
cache.contains_reverse(node as u32)
342+
false
337343
}
338344
} else {
339345
false
340346
}
341347
}
348+
349+
fn generate_expanded_parents(&self, node: usize) -> Vec<u32> {
350+
(0..self.expansion_degree)
351+
.filter_map(|i| {
352+
let other = self.correspondent(node, i);
353+
if self.reversed {
354+
if other > node {
355+
Some(other as u32)
356+
} else {
357+
None
358+
}
359+
} else if other < node {
360+
Some(other as u32)
361+
} else {
362+
None
363+
}
364+
})
365+
.collect()
366+
}
342367
}
343368

344369
impl<'a, H, G> ZigZag for ZigZagGraph<H, G>
345370
where
346371
H: Hasher,
347-
G: Graph<H>,
372+
G: Graph<H> + ParameterSetMetadata,
348373
{
349374
type BaseHasher = H;
350375
type BaseGraph = G;
@@ -395,43 +420,37 @@ where
395420
where
396421
F: FnMut(&Vec<u32>) -> T,
397422
{
398-
if !self.contains_parents_cache(node) {
399-
let parents: Vec<u32> = (0..self.expansion_degree)
400-
.filter_map(|i| {
401-
let other = self.correspondent(node, i);
402-
if self.reversed {
403-
if other > node {
404-
Some(other as u32)
405-
} else {
406-
None
407-
}
408-
} else if other < node {
409-
Some(other as u32)
410-
} else {
411-
None
412-
}
413-
})
414-
.collect();
415-
416-
if let Some(ref cache) = self.parents_cache {
417-
if self.forward() {
418-
cache.write_forward(node as u32, parents);
419-
} else {
420-
cache.write_reverse(node as u32, parents);
421-
}
422-
} else {
423-
return cb(&parents);
424-
}
423+
if !self.use_cache {
424+
// No cache usage, generate on demand.
425+
return cb(&self.generate_expanded_parents(node));
425426
}
426427

427-
if let Some(ref cache) = self.parents_cache {
428+
// Check if we need to fill the cache.
429+
if !self.contains_parents_cache(node) {
430+
// Cache is empty so we need to generate the parents.
431+
let parents = self.generate_expanded_parents(node);
432+
433+
// Store the newly generated cached value.
434+
let mut cache_lock = PARENT_CACHE.write().unwrap();
435+
let cache = cache_lock
436+
.get_mut(&self.id)
437+
.expect("Invalid cache construction");
428438
if self.forward() {
429-
cache.read_forward(node as u32, |parents| cb(parents.unwrap()))
439+
cache.write_forward(node as u32, parents);
430440
} else {
431-
cache.read_reverse(node as u32, |parents| cb(parents.unwrap()))
441+
cache.write_reverse(node as u32, parents);
432442
}
443+
}
444+
445+
// We made sure the cache is filled above, now we can return the value.
446+
let cache_lock = PARENT_CACHE.read().unwrap();
447+
let cache = cache_lock
448+
.get(&self.id)
449+
.expect("Invalid cache construction");
450+
if self.forward() {
451+
cache.read_forward(node as u32, |parents| cb(parents.unwrap()))
433452
} else {
434-
unreachable!()
453+
cache.read_reverse(node as u32, |parents| cb(parents.unwrap()))
435454
}
436455
}
437456

0 commit comments

Comments
 (0)