Skip to content

Commit 5285bcb

Browse files
0x0ecemmcgee-jump
authored andcommitted
simd-0180: vote-based leader schedule in frankendancer
1 parent 5621abe commit 5285bcb

30 files changed

+372
-128
lines changed

agave

src/app/fdctl/topology.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fd_topo_initialize( config_t * config ) {
9090
otherwise cause drops. */
9191
/**/ fd_topob_link( topo, "dedup_resolv", "dedup_resolv", 65536UL, FD_TPU_PARSED_MTU, 1UL );
9292
FOR(resolv_tile_cnt) fd_topob_link( topo, "resolv_pack", "resolv_pack", 65536UL, FD_TPU_RESOLVED_MTU, 1UL );
93-
/**/ fd_topob_link( topo, "stake_out", "stake_out", 128UL, 40UL + 40200UL * 40UL, 1UL );
93+
/**/ fd_topob_link( topo, "stake_out", "stake_out", 128UL, FD_STAKE_OUT_MTU, 1UL );
9494
/* pack_bank is shared across all banks, so if one bank stalls due to complex transactions, the buffer neeeds to be large so that
9595
other banks can keep proceeding. */
9696
/**/ fd_topob_link( topo, "pack_bank", "pack_bank", 65536UL, USHORT_MAX, 1UL );

src/app/firedancer/topology.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ fd_topo_initialize( config_t * config ) {
334334
FOR(verify_tile_cnt) fd_topob_link( topo, "verify_dedup", "verify_dedup", config->tiles.verify.receive_buffer_size, FD_TPU_PARSED_MTU, 1UL );
335335
/**/ fd_topob_link( topo, "dedup_pack", "dedup_pack", config->tiles.verify.receive_buffer_size, FD_TPU_PARSED_MTU, 1UL );
336336

337-
/**/ fd_topob_link( topo, "stake_out", "stake_out", 128UL, 40UL + 40200UL * 40UL, 1UL );
337+
/**/ fd_topob_link( topo, "stake_out", "stake_out", 128UL, FD_STAKE_OUT_MTU, 1UL );
338338

339339
FOR(shred_tile_cnt) fd_topob_link( topo, "shred_sign", "shred_sign", 128UL, 32UL, 1UL );
340340
FOR(shred_tile_cnt) fd_topob_link( topo, "sign_shred", "sign_shred", 128UL, 64UL, 1UL );

src/disco/gui/fd_gui.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "../../disco/genesis/fd_genesis_cluster.h"
1010
#include "../../disco/pack/fd_pack.h"
1111
#include "../../disco/pack/fd_pack_cost.h"
12+
#include "../../disco/shred/fd_stake_ci.h"
1213

1314
FD_FN_CONST ulong
1415
fd_gui_align( void ) {
@@ -1076,29 +1077,33 @@ fd_gui_handle_leader_schedule( fd_gui_t * gui,
10761077
ulong start_slot = msg[ 2 ];
10771078
ulong slot_cnt = msg[ 3 ];
10781079
ulong excluded_stake = msg[ 4 ];
1080+
ulong vote_keyed_lsched = msg[ 5 ];
10791081

1080-
FD_TEST( staked_cnt<=50000UL );
1081-
FD_TEST( slot_cnt<=432000UL );
1082+
FD_TEST( staked_cnt<=MAX_STAKED_LEADERS );
1083+
FD_TEST( slot_cnt<=MAX_SLOTS_PER_EPOCH );
10821084

10831085
ulong idx = epoch % 2UL;
10841086
gui->epoch.has_epoch[ idx ] = 1;
10851087

1086-
10871088
gui->epoch.epochs[ idx ].epoch = epoch;
10881089
gui->epoch.epochs[ idx ].start_slot = start_slot;
10891090
gui->epoch.epochs[ idx ].end_slot = start_slot + slot_cnt - 1; // end_slot is inclusive.
10901091
gui->epoch.epochs[ idx ].excluded_stake = excluded_stake;
10911092
gui->epoch.epochs[ idx ].my_total_slots = 0UL;
10921093
gui->epoch.epochs[ idx ].my_skipped_slots = 0UL;
1094+
1095+
fd_vote_stake_weight_t const * stake_weights = fd_type_pun_const( msg+6UL );
1096+
memcpy( gui->epoch.epochs[ idx ].stakes, stake_weights, staked_cnt*sizeof(fd_vote_stake_weight_t) );
1097+
10931098
fd_epoch_leaders_delete( fd_epoch_leaders_leave( gui->epoch.epochs[ idx ].lsched ) );
10941099
gui->epoch.epochs[idx].lsched = fd_epoch_leaders_join( fd_epoch_leaders_new( gui->epoch.epochs[ idx ]._lsched,
10951100
epoch,
10961101
gui->epoch.epochs[ idx ].start_slot,
10971102
slot_cnt,
10981103
staked_cnt,
1099-
fd_type_pun_const( msg+5UL ),
1100-
excluded_stake ) );
1101-
fd_memcpy( gui->epoch.epochs[ idx ].stakes, fd_type_pun_const( msg+5UL ), staked_cnt*sizeof(gui->epoch.epochs[ idx ].stakes[ 0 ]) );
1104+
gui->epoch.epochs[ idx ].stakes,
1105+
excluded_stake,
1106+
vote_keyed_lsched ) );
11021107

11031108
if( FD_UNLIKELY( start_slot==0UL ) ) {
11041109
gui->epoch.epochs[ 0 ].start_time = fd_log_wallclock();

src/disco/gui/fd_gui.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,8 @@ struct fd_gui {
387387
ulong end_slot;
388388
ulong excluded_stake;
389389
fd_epoch_leaders_t * lsched;
390-
uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) _lsched[ FD_EPOCH_LEADERS_FOOTPRINT(50000UL, 432000UL) ];
391-
fd_stake_weight_t stakes[ 50000UL ];
390+
uchar __attribute__((aligned(FD_EPOCH_LEADERS_ALIGN))) _lsched[ FD_EPOCH_LEADERS_FOOTPRINT(MAX_STAKED_LEADERS, MAX_SLOTS_PER_EPOCH) ];
391+
fd_vote_stake_weight_t stakes[ MAX_STAKED_LEADERS ];
392392
} epochs[ 2 ];
393393
} epoch;
394394

src/disco/gui/fd_gui_printf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ fd_gui_printf_epoch( fd_gui_t * gui,
536536
jsonp_close_array( gui );
537537

538538
jsonp_open_array( gui, "staked_lamports" );
539-
fd_stake_weight_t * stakes = gui->epoch.epochs[epoch_idx].stakes;
539+
fd_vote_stake_weight_t * stakes = gui->epoch.epochs[epoch_idx].stakes;
540540
for( ulong i=0UL; i<lsched->pub_cnt; i++ ) jsonp_ulong_as_str( gui, NULL, stakes[ i ].stake );
541541
jsonp_close_array( gui );
542542

src/disco/gui/fd_gui_tile.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ during_frag( fd_gui_ctx_t * ctx,
191191
sz = 8UL + peer_cnt*112UL;
192192
} else if( FD_UNLIKELY( sig==FD_PLUGIN_MSG_LEADER_SCHEDULE ) ) {
193193
ulong staked_cnt = ((ulong *)src)[ 1 ];
194-
FD_TEST( staked_cnt<=50000UL );
195-
sz = 40UL + staked_cnt*40UL;
194+
FD_TEST( staked_cnt<=MAX_STAKED_LEADERS );
195+
sz = fd_stake_weight_msg_sz( staked_cnt );
196196
}
197197

198198
if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>sizeof( ctx->buf ) ) )

src/disco/plugin/fd_plugin_tile.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ typedef struct {
2929
ulong out_chunk0;
3030
ulong out_wmark;
3131
ulong out_chunk;
32+
33+
ulong sz; /* size of payload computed in during_frag and passed to after_frag */
3234
} fd_plugin_ctx_t;
3335

3436
FD_FN_CONST static inline ulong
@@ -56,25 +58,25 @@ during_frag( fd_plugin_ctx_t * ctx,
5658
uchar * src = (uchar *)fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk );
5759
ulong * dst = (ulong *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
5860

59-
/* ... todo... sigh, sz is not correct since it's too big */
61+
ctx->sz = sz;
6062
if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_GOSSIP && sig==FD_PLUGIN_MSG_GOSSIP_UPDATE ) ) {
6163
ulong peer_cnt = ((ulong *)src)[ 0 ];
6264
FD_TEST( peer_cnt<=40200 );
63-
sz = 8UL + peer_cnt*FD_GOSSIP_LINK_MSG_SIZE;
65+
ctx->sz = 8UL + peer_cnt*FD_GOSSIP_LINK_MSG_SIZE;
6466
} else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_GOSSIP || ctx->in_kind[ in_idx ]==IN_KIND_POH || ctx->in_kind[ in_idx ]==IN_KIND_VOTE ) && FD_LIKELY( sig==FD_PLUGIN_MSG_VOTE_ACCOUNT_UPDATE ) ) {
6567
ulong peer_cnt = ((ulong *)src)[ 0 ];
6668
FD_TEST( peer_cnt<=40200 );
67-
sz = 8UL + peer_cnt*112UL;
69+
ctx->sz = 8UL + peer_cnt*112UL;
6870
} else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_STAKE ) ) {
6971
ulong staked_cnt = ((ulong *)src)[ 1 ];
70-
FD_TEST( staked_cnt<=50000UL );
71-
sz = 40UL + staked_cnt*40UL;
72+
FD_TEST( staked_cnt<=MAX_STAKED_LEADERS );
73+
ctx->sz = fd_stake_weight_msg_sz( staked_cnt );
7274
}
7375

7476
if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
75-
FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
77+
FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, ctx->sz, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
7678

77-
fd_memcpy( dst, src, sz );
79+
fd_memcpy( dst, src, ctx->sz );
7880
}
7981

8082
static inline void
@@ -86,6 +88,7 @@ after_frag( fd_plugin_ctx_t * ctx,
8688
ulong tsorig,
8789
ulong tspub,
8890
fd_stem_context_t * stem ) {
91+
(void)sz;
8992
(void)seq;
9093
(void)tsorig;
9194
(void)tspub;
@@ -130,12 +133,8 @@ after_frag( fd_plugin_ctx_t * ctx,
130133
default: FD_LOG_ERR(( "bad in_idx" ));
131134
}
132135

133-
ulong true_size = sz;
134-
if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_GOSSIP || ( ctx->in_kind[ in_idx ]==IN_KIND_VOTE ) ) ) true_size = 8UL + 40200UL*(58UL+12UL*34UL);
135-
else if( FD_UNLIKELY( ctx->in_kind[ in_idx ]==IN_KIND_STAKE ) ) true_size = 40UL + 40200UL*40UL; /* ... todo... sigh, sz is not correct since it's too big */
136-
137-
fd_stem_publish( stem, 0UL, sig, ctx->out_chunk, sz, 0UL, 0UL, 0UL ); /* Not true_sz which might not fit */
138-
ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, true_size, ctx->out_chunk0, ctx->out_wmark );
136+
fd_stem_publish( stem, 0UL, sig, ctx->out_chunk, ctx->sz, 0UL, 0UL, 0UL );
137+
ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, ctx->sz, ctx->out_chunk0, ctx->out_wmark );
139138
}
140139

141140
static void

src/disco/shred/fd_shred_dest.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,13 @@ fd_shred_dest_new( void * mem,
108108

109109
pubkey_to_idx_t * pubkey_to_idx_map = pubkey_to_idx_join( pubkey_to_idx_new( _map, lg_cnt ) );
110110
for( ulong i=0UL; i<cnt; i++ ) {
111-
pubkey_to_idx_insert( pubkey_to_idx_map, info[i].pubkey )->idx = i;
111+
/* we should never have duplicates in info[i].pubkey, but in case
112+
of duplicates it's better to skip than to segfault. */
113+
pubkey_to_idx_t * inserted = pubkey_to_idx_insert( pubkey_to_idx_map, info[i].pubkey );
114+
if( FD_UNLIKELY( !inserted ) ) {
115+
continue;
116+
}
117+
inserted->idx = i;
112118
}
113119
pubkey_to_idx_t * query = pubkey_to_idx_query( pubkey_to_idx_map, *source, NULL );
114120
if( FD_UNLIKELY( !query ) ) {

src/disco/shred/fd_stake_ci.c

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66
#define SORT_BEFORE(a,b) (memcmp( (a).pubkey.uc, (b).pubkey.uc, 32UL )<0)
77
#include "../../util/tmpl/fd_sort.c"
88

9+
#define SORT_NAME sort_weights_by_stake_id
10+
#define SORT_KEY_T fd_stake_weight_t
11+
#define SORT_BEFORE(a,b) ((a).stake > (b).stake ? 1 : ((a).stake < (b).stake ? 0 : memcmp( (a).key.uc, (b).key.uc, 32UL )>0))
12+
#include "../../util/tmpl/fd_sort.c"
13+
14+
#define SORT_NAME sort_weights_by_id
15+
#define SORT_KEY_T fd_stake_weight_t
16+
#define SORT_BEFORE(a,b) (memcmp( (a).key.uc, (b).key.uc, 32UL )>0)
17+
#include "../../util/tmpl/fd_sort.c"
18+
919
/* We don't have or need real contact info for the local validator, but
1020
we want to be able to distinguish it from staked nodes with no
1121
contact info. */
@@ -16,20 +26,21 @@ fd_stake_ci_new( void * mem,
1626
fd_pubkey_t const * identity_key ) {
1727
fd_stake_ci_t * info = (fd_stake_ci_t *)mem;
1828

19-
fd_stake_weight_t dummy_stakes[ 1 ] = {{ .key = {{0}}, .stake = 1UL }};
29+
fd_vote_stake_weight_t dummy_stakes[ 1 ] = {{ .vote_key = {{0}}, .id_key = {{0}}, .stake = 1UL }};
2030
fd_shred_dest_weighted_t dummy_dests[ 1 ] = {{ .pubkey = *identity_key, .ip4 = SELF_DUMMY_IP }};
2131

2232
/* Initialize first 2 to satisfy invariants */
23-
info->stake_weight[ 0 ] = dummy_stakes[ 0 ];
33+
info->vote_stake_weight[ 0 ] = dummy_stakes[ 0 ];
2434
info->shred_dest [ 0 ] = dummy_dests [ 0 ];
2535
for( ulong i=0UL; i<2UL; i++ ) {
2636
fd_per_epoch_info_t * ei = info->epoch_info + i;
2737
ei->epoch = i;
2838
ei->start_slot = 0UL;
2939
ei->slot_cnt = 0UL;
3040
ei->excluded_stake = 0UL;
41+
ei->vote_keyed_lsched = 0UL;
3142

32-
ei->lsched = fd_epoch_leaders_join( fd_epoch_leaders_new( ei->_lsched, 0UL, 0UL, 1UL, 1UL, info->stake_weight, 0UL ) );
43+
ei->lsched = fd_epoch_leaders_join( fd_epoch_leaders_new( ei->_lsched, 0UL, 0UL, 1UL, 1UL, info->vote_stake_weight, 0UL, ei->vote_keyed_lsched ) );
3344
ei->sdest = fd_shred_dest_join ( fd_shred_dest_new ( ei->_sdest, info->shred_dest, 1UL, ei->lsched, identity_key, 0UL ) );
3445
}
3546
info->identity_key[ 0 ] = *identity_key;
@@ -55,8 +66,9 @@ fd_stake_ci_stake_msg_init( fd_stake_ci_t * info,
5566
info->scratch->slot_cnt = msg->slot_cnt;
5667
info->scratch->staked_cnt = msg->staked_cnt;
5768
info->scratch->excluded_stake = msg->excluded_stake;
69+
info->scratch->vote_keyed_lsched = msg->vote_keyed_lsched;
5870

59-
fd_memcpy( info->stake_weight, msg->weights, msg->staked_cnt*sizeof(fd_stake_weight_t) );
71+
fd_memcpy( info->vote_stake_weight, msg->weights, msg->staked_cnt*sizeof(fd_vote_stake_weight_t) );
6072
}
6173

6274
static inline void
@@ -78,6 +90,40 @@ log_summary( char const * msg, fd_stake_ci_t * info ) {
7890
#endif
7991
}
8092

93+
ulong
94+
compute_id_weights_from_vote_weights( fd_stake_weight_t * stake_weight,
95+
fd_vote_stake_weight_t const * vote_stake_weight,
96+
ulong staked_cnt ) {
97+
/* Copy from input message [(vote, id, stake)] into old format [(id, stake)]. */
98+
for( ulong i=0UL; i<staked_cnt; i++ ) {
99+
memcpy( stake_weight[ i ].key.uc, vote_stake_weight[ i ].id_key.uc, sizeof(fd_pubkey_t) );
100+
stake_weight[ i ].stake = vote_stake_weight[ i ].stake;
101+
}
102+
103+
/* Sort [(id, stake)] by id, so we can dedup */
104+
sort_weights_by_id_inplace( stake_weight, staked_cnt );
105+
106+
/* Dedup entries, aggregating stake */
107+
ulong j=0UL;
108+
for( ulong i=1UL; i<staked_cnt; i++ ) {
109+
fd_pubkey_t * pre = &stake_weight[ j ].key;
110+
fd_pubkey_t * cur = &stake_weight[ i ].key;
111+
if( 0==memcmp( pre, cur, sizeof(fd_pubkey_t) ) ) {
112+
stake_weight[ j ].stake += stake_weight[ i ].stake;
113+
} else {
114+
++j;
115+
stake_weight[ j ].stake = stake_weight[ i ].stake;
116+
memcpy( stake_weight[ j ].key.uc, stake_weight[ i ].key.uc, sizeof(fd_pubkey_t) );
117+
}
118+
}
119+
ulong staked_cnt_by_id = fd_ulong_min( staked_cnt, j+1 );
120+
121+
/* Sort [(id, stake)] by stake then id, as expected */
122+
sort_weights_by_stake_id_inplace( stake_weight, staked_cnt_by_id );
123+
124+
return staked_cnt_by_id;
125+
}
126+
81127
#define SET_NAME unhit_set
82128
#define SET_MAX MAX_SHRED_DESTS
83129
#include "../../util/tmpl/fd_set.c"
@@ -90,6 +136,8 @@ fd_stake_ci_stake_msg_fini( fd_stake_ci_t * info ) {
90136
and whatever contact info we previously knew. */
91137
ulong epoch = info->scratch->epoch;
92138
ulong staked_cnt = info->scratch->staked_cnt;
139+
ulong unchanged_staked_cnt = info->scratch->staked_cnt;
140+
ulong vote_keyed_lsched = info->scratch->vote_keyed_lsched;
93141

94142
/* Just take the first one arbitrarily because they both have the same
95143
contact info, other than possibly some staked nodes with no contact
@@ -106,6 +154,8 @@ fd_stake_ci_stake_msg_fini( fd_stake_ci_t * info ) {
106154
unhit_set_t * unhit = unhit_set_join( unhit_set_new( _unhit ) );
107155
unhit_set_full( unhit );
108156

157+
staked_cnt = compute_id_weights_from_vote_weights( info->stake_weight, info->vote_stake_weight, staked_cnt );
158+
109159
for( ulong i=0UL; i<staked_cnt; i++ ) {
110160
fd_shred_dest_idx_t old_idx = fd_shred_dest_pubkey_to_idx( existing_sdest, &(info->stake_weight[ i ].key) );
111161
fd_shred_dest_weighted_t * in_prev = fd_shred_dest_idx_to_dest( existing_sdest, old_idx );
@@ -159,11 +209,12 @@ fd_stake_ci_stake_msg_fini( fd_stake_ci_t * info ) {
159209
new_ei->start_slot = info->scratch->start_slot;
160210
new_ei->slot_cnt = info->scratch->slot_cnt;
161211
new_ei->excluded_stake = excluded_stake;
212+
new_ei->vote_keyed_lsched = vote_keyed_lsched;
162213

163214
new_ei->lsched = fd_epoch_leaders_join( fd_epoch_leaders_new( new_ei->_lsched, epoch, new_ei->start_slot, new_ei->slot_cnt,
164-
staked_cnt, info->stake_weight, excluded_stake ) );
215+
unchanged_staked_cnt, info->vote_stake_weight, excluded_stake, vote_keyed_lsched ) );
165216
new_ei->sdest = fd_shred_dest_join ( fd_shred_dest_new ( new_ei->_sdest, info->shred_dest, j,
166-
new_ei->lsched, info->identity_key, excluded_stake ) );
217+
new_ei->lsched, info->identity_key, excluded_stake ) );
167218
log_summary( "stake update", info );
168219
}
169220

0 commit comments

Comments
 (0)