Line data Source code
1 : #define _GNU_SOURCE
2 :
3 : #include "fd_accdb.h"
4 : #include "../../util/fd_util.h"
5 :
6 : #include <stdlib.h>
7 : #include <string.h>
8 : #include <unistd.h>
9 : #include <sys/mman.h>
10 :
11 : /* bench_accdb_hotread: populate a small set of accounts so they all fit
12 : in cache, then hammer acquire+release read-only in a tight loop for a
13 : fixed duration. This isolates the hot-path cost of in-cache reads
14 : without any disk I/O, eviction, or write-back noise. */
15 :
16 : static uchar dummy_owner[ 32 ] = { 0xEE };
17 :
18 : #define BENCH_MAX_DATA_SZ (256UL) /* token-account sized */
19 0 : #define BENCH_CACHE_FOOTPRINT (16UL<<30UL)
20 :
21 : static uchar data_buf[ BENCH_MAX_DATA_SZ ];
22 :
23 : static void
24 : make_pubkey( uchar pubkey[ static 32 ],
25 0 : ulong idx ) {
26 0 : fd_memset( pubkey, 0, 32UL );
27 0 : fd_memcpy( pubkey, &idx, sizeof(ulong) );
28 0 : }
29 :
30 : static fd_accdb_t *
31 : bench_setup( int * out_fd,
32 : ulong max_accounts,
33 : ulong max_live_slots,
34 : ulong max_account_writes_per_slot,
35 : ulong partition_cnt,
36 0 : ulong partition_sz ) {
37 0 : int fd = memfd_create( "accdb_hotread", 0 );
38 0 : if( FD_UNLIKELY( fd<0 ) ) FD_LOG_ERR(( "memfd_create failed" ));
39 0 : *out_fd = fd;
40 :
41 0 : ulong cache_fp = BENCH_CACHE_FOOTPRINT;
42 0 : ulong shmem_fp = fd_accdb_shmem_footprint( max_accounts, max_live_slots, max_account_writes_per_slot, partition_cnt, cache_fp, 640UL, 1UL );
43 0 : FD_TEST( shmem_fp );
44 0 : void * shmem_mem = aligned_alloc( fd_accdb_shmem_align(), shmem_fp );
45 0 : FD_TEST( shmem_mem );
46 0 : fd_accdb_shmem_t * shmem = fd_accdb_shmem_join(
47 0 : fd_accdb_shmem_new( shmem_mem, max_accounts, max_live_slots,
48 0 : max_account_writes_per_slot, partition_cnt,
49 0 : partition_sz, cache_fp, 640UL, 0, 42UL, 1UL ) );
50 0 : FD_TEST( shmem );
51 :
52 0 : ulong accdb_fp = fd_accdb_footprint( max_live_slots );
53 0 : FD_TEST( accdb_fp );
54 0 : void * accdb_mem = aligned_alloc( fd_accdb_align(), accdb_fp );
55 0 : FD_TEST( accdb_mem );
56 0 : fd_accdb_t * accdb = fd_accdb_join( fd_accdb_new( accdb_mem, shmem, fd, 0UL, NULL ) );
57 0 : FD_TEST( accdb );
58 0 : return accdb;
59 0 : }
60 :
61 : int
62 : main( int argc,
63 : char ** argv ) {
64 : fd_boot( &argc, &argv );
65 :
66 : ulong account_cnt = fd_env_strip_cmdline_ulong( &argc, &argv, "--accounts", NULL, 10000UL );
67 : ulong duration_ns = fd_env_strip_cmdline_ulong( &argc, &argv, "--duration", NULL, 15000000000UL ); /* 15 s */
68 : uint seed = fd_env_strip_cmdline_uint ( &argc, &argv, "--seed", NULL, 42U );
69 :
70 : FD_LOG_NOTICE(( "accdb hot-read bench (accounts=%lu duration=%.1f s seed=%u)",
71 : account_cnt, (double)duration_ns/1e9, seed ));
72 :
73 : fd_rng_t _rng[1];
74 : fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, seed, 0UL ) );
75 : FD_TEST( rng );
76 :
77 : /* Setup */
78 : int fd;
79 : ulong partition_sz = 1UL<<30UL;
80 : ulong partition_cnt = 1024UL;
81 : fd_accdb_t * accdb = bench_setup( &fd,
82 : account_cnt + 1024UL,
83 : 64UL,
84 : (uint)account_cnt + 1024U,
85 : partition_cnt,
86 : partition_sz );
87 :
88 : fd_accdb_fork_id_t root = fd_accdb_attach_child( accdb, (fd_accdb_fork_id_t){ .val = USHORT_MAX } );
89 : fd_accdb_fork_id_t fork = fd_accdb_attach_child( accdb, root );
90 :
91 : /* Populate accounts so they are all in cache */
92 : uchar pubkey[ 32 ];
93 : for( ulong i=0UL; i<account_cnt; i++ ) {
94 : make_pubkey( pubkey, i );
95 : ulong sz = 165UL; /* token account */
96 :
97 : uchar const * pks[1] = { pubkey };
98 : int wr[1] = { 1 };
99 : fd_acc_t acc[1];
100 : memset( acc, 0, sizeof(acc) );
101 : fd_accdb_acquire( accdb, fork, 1UL, pks, wr, acc );
102 : acc[0].lamports = i+1UL;
103 : acc[0].data_len = sz;
104 : memcpy( acc[0].owner, dummy_owner, 32UL );
105 : memcpy( acc[0].data, data_buf, sz );
106 : acc[0].commit = 1;
107 : fd_accdb_release( accdb, 1UL, acc );
108 : }
109 :
110 : /* Warm: read every account once to ensure cache residency */
111 : for( ulong i=0UL; i<account_cnt; i++ ) {
112 : make_pubkey( pubkey, i );
113 : uchar const * pks[1] = { pubkey };
114 : int wr[1] = { 0 };
115 : fd_acc_t acc[1];
116 : memset( acc, 0, sizeof(acc) );
117 : fd_accdb_acquire( accdb, fork, 1UL, pks, wr, acc );
118 : fd_accdb_release( accdb, 1UL, acc );
119 : }
120 :
121 : FD_LOG_NOTICE(( "populated %lu accounts, starting hot read loop", account_cnt ));
122 :
123 : /* Hot loop: read-only acquire+release for duration_ns */
124 : ulong ops = 0UL;
125 : long start = fd_log_wallclock();
126 : long stop = start + (long)duration_ns;
127 :
128 : while( fd_log_wallclock()<stop ) {
129 : /* Batch of 1000 to amortize the clock call */
130 : for( ulong b=0UL; b<1000UL; b++ ) {
131 : ulong idx = fd_rng_ulong( rng ) % account_cnt;
132 : make_pubkey( pubkey, idx );
133 :
134 : uchar const * pks[1] = { pubkey };
135 : int wr[1] = { 0 };
136 : fd_acc_t acc[1];
137 : memset( acc, 0, sizeof(acc) );
138 : fd_accdb_acquire( accdb, fork, 1UL, pks, wr, acc );
139 : fd_accdb_release( accdb, 1UL, acc );
140 : ops++;
141 : }
142 : }
143 :
144 : long elapsed = fd_log_wallclock() - start;
145 : double secs = (double)elapsed / 1e9;
146 :
147 : FD_LOG_NOTICE(( "hot-read: %lu ops in %.3f s (%.0f ops/s, %.0f ns/op)",
148 : ops, secs,
149 : (double)ops / secs,
150 : (double)elapsed / (double)ops ));
151 :
152 : fd_rng_delete( fd_rng_leave( rng ) );
153 : close( fd );
154 :
155 : FD_LOG_NOTICE(( "pass" ));
156 : fd_halt();
157 : return 0;
158 : }
|