@@ -6,7 +6,9 @@ use criterion::Criterion;
66use criterion:: criterion_group;
77use criterion:: criterion_main;
88use octez_riscv_durable_storage:: database:: Database ;
9+ use octez_riscv_durable_storage:: database:: DatabaseError ;
910use octez_riscv_durable_storage:: merkle_layer:: Key ;
11+ use octez_riscv_durable_storage:: persistence_layer:: PersistenceLayerError ;
1012use octez_riscv_durable_storage:: persistence_layer:: utils:: TestableTmpdir ;
1113use octez_riscv_durable_storage:: random:: generate_keys;
1214use octez_riscv_durable_storage:: random:: generate_random_bytes;
@@ -15,48 +17,63 @@ use rand::prelude::*;
1517use rand:: rng;
1618use tokio:: runtime:: Handle ;
1719
18- struct BenchmarkState {
20+ #[ cfg_attr(
21+ not( feature = "bench_cloning" ) ,
22+ expect(
23+ dead_code,
24+ reason = "The handle and repo are only used for cloning the database"
25+ )
26+ ) ]
27+ struct BenchmarkState < ' a > {
1928 database : Database ,
20- large_read_buffer : Vec < u8 > ,
21- large_read_choices : Vec < usize > ,
22- prepopulated_large_node_keys : Vec < Key > ,
29+ operations : Vec < Operation > ,
2330 prepopulated_node_keys : Vec < Key > ,
2431 prepopulated_values : Vec < Bytes > ,
25- small_read_buffer : Vec < u8 > ,
26- small_read_choices : Vec < usize > ,
27- small_write_choices : Vec < ( usize , usize ) > ,
32+ read_buffer : Vec < u8 > ,
33+ handle : & ' a Handle ,
34+ repo : & ' a DirectoryManager ,
2835}
2936
30- impl BenchmarkState {
31- fn clone_with ( & self , handle : & Handle , repo : & DirectoryManager ) -> Self {
37+ impl < ' a > BenchmarkState < ' a > {
38+ fn clone_with ( & self , handle : & ' a Handle , repo : & ' a DirectoryManager ) -> Self {
3239 Self {
3340 database : self
3441 . database
3542 . try_clone_with ( handle, repo)
3643 . expect ( "Cloning the database should work" ) ,
37- large_read_buffer : vec ! [ 0u8 ; LARGE_READ_MIN_SIZE ] ,
38- large_read_choices : self . large_read_choices . clone ( ) ,
39- prepopulated_large_node_keys : self . prepopulated_large_node_keys . clone ( ) ,
44+ operations : self . operations . clone ( ) ,
45+ read_buffer : vec ! [ 0u8 ; LARGE_READ_MAX_SIZE ] ,
4046 prepopulated_node_keys : self . prepopulated_node_keys . clone ( ) ,
4147 prepopulated_values : self . prepopulated_values . clone ( ) ,
42- small_read_buffer : vec ! [ 0u8 ; SMALL_READ_SIZE ] ,
43- small_read_choices : self . small_read_choices . clone ( ) ,
44- small_write_choices : self . small_write_choices . clone ( ) ,
48+ handle,
49+ repo,
4550 }
4651 }
4752}
4853
54+ #[ derive( Clone ) ]
55+ #[ expect( dead_code, reason = "Some operations aren't benchmarked yet" ) ]
56+ enum Operation {
57+ Clone ,
58+ Delete { key_index : usize } ,
59+ Exists { key_index : usize } ,
60+ Hash ,
61+ Read { key_index : usize , size : usize } ,
62+ ValueLength { key_index : usize } ,
63+ Write { key_index : usize , data_index : usize } ,
64+ }
65+
4966const BLOCK_FREQUENCY : usize = 5_000 ;
5067const ERC_20_TRANSACTIONS : usize = 10_000 ;
51- const LARGE_READ_MIN_SIZE : usize = 48000 ;
68+ const LARGE_READ_MIN_SIZE : usize = 96000 ;
5269const LARGE_READ_MAX_SIZE : usize = LARGE_READ_MIN_SIZE * 2 ;
5370const PREPOPULATED_LARGE_NODE_KEYS_COUNT : usize = 100 ;
5471const PREPOPULATED_NODE_KEYS_COUNT : usize = 10_000_000 - PREPOPULATED_LARGE_NODE_KEYS_COUNT ;
5572const SMALL_READS : usize = 13 ;
5673const SMALL_READ_SIZE : usize = 32 ;
5774const SMALL_WRITES : usize = 7 ;
5875
59- fn setup_benchmark_state ( handle : & Handle , repo : & DirectoryManager ) -> BenchmarkState {
76+ fn setup_benchmark_state < ' a > ( handle : & ' a Handle , repo : & ' a DirectoryManager ) -> BenchmarkState < ' a > {
6077 let mut database = Database :: try_new ( handle, repo) . expect ( "Creating a database should succeed" ) ;
6178 let mut rng = rng ( ) ;
6279
@@ -92,68 +109,135 @@ fn setup_benchmark_state(handle: &Handle, repo: &DirectoryManager) -> BenchmarkS
92109 prepopulated_large_node_keys. push ( key) ;
93110 }
94111
95- let large_read_choices = ( 0 ..ERC_20_TRANSACTIONS )
96- . map ( |_| rng. random_range ( 0 ..prepopulated_large_node_keys. len ( ) ) )
97- . collect ( ) ;
112+ let mut operations =
113+ Vec :: < Operation > :: with_capacity ( ERC_20_TRANSACTIONS * ( 1 + SMALL_READS + SMALL_WRITES ) ) ;
114+
115+ for i in 0 ..ERC_20_TRANSACTIONS {
116+ let key_index = rng. random_range ( 0 ..prepopulated_large_node_keys. len ( ) ) ;
117+ let size = rng. random_range ( LARGE_READ_MIN_SIZE ..LARGE_READ_MAX_SIZE ) ;
118+ let operation = Operation :: Read { key_index, size } ;
98119
99- let small_read_choices = ( 0 ..ERC_20_TRANSACTIONS * SMALL_READS )
100- . map ( |_| rng. random_range ( 0 ..prepopulated_node_keys. len ( ) ) )
101- . collect ( ) ;
120+ let mut vec = vec ! [ 0u8 ; size] ;
121+ rng. fill ( vec. as_mut_slice ( ) ) ;
102122
103- let small_write_choices = ( 0 ..ERC_20_TRANSACTIONS * SMALL_WRITES )
104- . map ( |_| {
105- (
106- rng. random_range ( 0 ..prepopulated_node_keys. len ( ) ) ,
107- rng. random_range ( 0 ..prepopulated_values. len ( ) ) ,
123+ // Write to the node so there's something meaningful and large to read
124+ database
125+ . write (
126+ prepopulated_large_node_keys[ key_index] . clone ( ) ,
127+ 0 ,
128+ Bytes :: from ( vec) ,
108129 )
109- } )
110- . collect ( ) ;
130+ . expect ( "The write should succeed" ) ;
131+ operations. push ( operation) ;
132+
133+ for _ in 0 ..SMALL_READS {
134+ let key_index = rng. random_range ( 0 ..prepopulated_large_node_keys. len ( ) ) ;
135+ let size = SMALL_READ_SIZE ;
136+ operations. push ( Operation :: Read { key_index, size } ) ;
137+
138+ let mut vec = vec ! [ 0u8 ; size] ;
139+ rng. fill ( vec. as_mut_slice ( ) ) ;
140+
141+ // Write to the node so there's something meaningful to read
142+ database
143+ . write (
144+ prepopulated_node_keys[ key_index] . clone ( ) ,
145+ 0 ,
146+ Bytes :: from ( vec) ,
147+ )
148+ . expect ( "The write should succeed" ) ;
149+ }
150+
151+ for _ in 0 ..SMALL_WRITES {
152+ operations. push ( Operation :: Write {
153+ key_index : rng. random_range ( 0 ..prepopulated_node_keys. len ( ) ) ,
154+ data_index : rng. random_range ( 0 ..prepopulated_values. len ( ) ) ,
155+ } ) ;
156+ }
157+
158+ if i % BLOCK_FREQUENCY == 0 {
159+ operations. push ( Operation :: Hash ) ;
160+ }
161+ }
111162
112163 BenchmarkState {
113164 database,
114- large_read_buffer : vec ! [ 0u8 ; LARGE_READ_MAX_SIZE ] ,
115- large_read_choices,
116- prepopulated_large_node_keys,
165+ operations,
117166 prepopulated_node_keys,
118167 prepopulated_values,
119- small_read_buffer : vec ! [ 0u8 ; SMALL_READ_SIZE ] ,
120- small_read_choices ,
121- small_write_choices ,
168+ read_buffer : vec ! [ 0u8 ; LARGE_READ_MAX_SIZE ] ,
169+ handle ,
170+ repo ,
122171 }
123172}
124173
125174#[ inline( never) ]
126175fn bench_run ( mut state : BenchmarkState ) {
127- let large_node_keys = & state. prepopulated_large_node_keys ;
128- let node_keys = & state. prepopulated_node_keys ;
129- let values = & state. prepopulated_values ;
130-
131- for i in 0 ..ERC_20_TRANSACTIONS {
132- let key = & large_node_keys[ state. large_read_choices [ i] ] ;
133- state
134- . database
135- . read ( key, 0 , & mut state. large_read_buffer )
136- . expect ( "The read should succeed" ) ;
137-
138- for _ in 0 ..SMALL_READS {
139- let key = & node_keys[ state. small_read_choices [ i] ] ;
140- state
176+ // `KeyNotFound` errors are allowed, since the (conceptual, not std::hint) black box that is the transaction inputs may delete keys
177+ // which are later used.
178+ for operation in state. operations {
179+ match operation {
180+ Operation :: Clone => {
181+ // TODO
182+ // Remove this feature after resolving RV-855 (Persistence layer cloning is slow).
183+ #[ cfg( feature = "bench_cloning" ) ]
184+ black_box (
185+ state
186+ . database
187+ . try_clone_with ( state. handle , state. repo )
188+ . expect ( "The clone should succeed" ) ,
189+ ) ;
190+ }
191+ Operation :: Delete { key_index } => match state
141192 . database
142- . read ( key, 0 , & mut state. small_read_buffer )
143- . expect ( "The read should succeed" ) ;
144- }
145-
146- for _ in 0 ..SMALL_WRITES {
147- let key = node_keys[ state. small_write_choices [ i] . 0 ] . clone ( ) ;
148- let value = values[ state. small_write_choices [ i] . 1 ] . clone ( ) ;
149- state
193+ . delete ( state. prepopulated_node_keys [ key_index] . clone ( ) )
194+ {
195+ Ok ( _)
196+ | Err ( DatabaseError :: PersistenceLayer ( PersistenceLayerError :: KeyNotFound ) ) => { }
197+ Err ( e) => panic ! ( "The deletion should succeed: {e:?}" ) ,
198+ } ,
199+ Operation :: Exists { key_index } => {
200+ state
201+ . database
202+ . exists ( & state. prepopulated_node_keys [ key_index] )
203+ . expect ( "The existence check should succeed" ) ;
204+ }
205+ Operation :: Hash => {
206+ black_box ( state. database . hash ( ) ) ;
207+ }
208+ Operation :: Read { key_index, size } => {
209+ match state. database . read (
210+ & state. prepopulated_node_keys [ key_index] ,
211+ 0 ,
212+ & mut state. read_buffer [ 0 ..size] ,
213+ ) {
214+ Ok ( _)
215+ | Err ( DatabaseError :: PersistenceLayer ( PersistenceLayerError :: KeyNotFound ) ) => { }
216+ Err ( e) => panic ! ( "The read should succeed: {e:?}" ) ,
217+ }
218+ }
219+ Operation :: ValueLength { key_index } => match state
150220 . database
151- . write ( key, 0 , value)
152- . expect ( "The read should succeed" ) ;
153- }
154-
155- if i % BLOCK_FREQUENCY == 0 {
156- black_box ( state. database . hash ( ) ) ;
221+ . value_length ( & state. prepopulated_node_keys [ key_index] )
222+ {
223+ Ok ( _)
224+ | Err ( DatabaseError :: PersistenceLayer ( PersistenceLayerError :: KeyNotFound ) ) => { }
225+ Err ( e) => panic ! ( "The value length calculation should succeed: {e:?}" ) ,
226+ } ,
227+ Operation :: Write {
228+ key_index,
229+ data_index,
230+ } => {
231+ match state. database . write (
232+ state. prepopulated_node_keys [ key_index] . clone ( ) ,
233+ 0 ,
234+ state. prepopulated_values [ data_index] . clone ( ) ,
235+ ) {
236+ Ok ( _)
237+ | Err ( DatabaseError :: PersistenceLayer ( PersistenceLayerError :: KeyNotFound ) ) => { }
238+ Err ( e) => panic ! ( "The write should succeed: {e:?}" ) ,
239+ }
240+ }
157241 }
158242 }
159243}
0 commit comments