1+ use std:: collections:: HashMap ;
12use std:: marker:: PhantomData ;
23use std:: sync:: { Arc , RwLock } ;
34
@@ -11,75 +12,73 @@ use crate::settings;
1112/// The expansion degree used for ZigZag Graphs.
1213pub 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),
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>>;
115106impl < ' a , H , G > Layerable < H > for ZigZagGraph < H , G >
116107where
117108 H : Hasher ,
118- G : Graph < H > + ' static ,
109+ G : Graph < H > + ParameterSetMetadata + ' static ,
119110{
120111}
121112
122113impl < H , G > ZigZagGraph < H , G >
123114where
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 {
263265impl < ' a , H , G > ZigZagGraph < H , G >
264266where
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
344369impl < ' a , H , G > ZigZag for ZigZagGraph < H , G >
345370where
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