Skip to content

Commit 2c06319

Browse files
riptlripatel-fd
authored andcommitted
funk: remove root-level record linked list
Funk maintained a LRU-like linked list weaving through all records in the root txn (fd_funk_rec_t). This linked list list served no purpose other than to iterate through records at the root txn. However, it created painful DRAM traffic for record-level operations, particularly when rooting. It also inhibited some optimizations for doing fast parallel account loading into funk. Removing this linked list at the root-level simplifies code, provides an immediate speedup, and possibilities for future optimization.
1 parent fd7570e commit 2c06319

File tree

6 files changed

+94
-147
lines changed

6 files changed

+94
-147
lines changed

src/funk/fd_funk.c

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,6 @@ fd_funk_new( void * shmem,
119119
fd_funk_rec_pool_reset( rec_join, 0UL );
120120
funk->rec_ele_gaddr = fd_wksp_gaddr_fast( wksp, rec_ele );
121121
funk->rec_max = (uint)rec_max;
122-
funk->rec_head_idx = FD_FUNK_REC_IDX_NULL;
123-
funk->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
124122

125123
funk->alloc_gaddr = fd_wksp_gaddr_fast( wksp, fd_alloc_join( fd_alloc_new( alloc, wksp_tag ), 0UL ) );
126124

@@ -402,23 +400,6 @@ fd_funk_verify( fd_funk_t * join ) {
402400
TEST( rec_chain_cnt==fd_funk_rec_map_chain_cnt( rec_map ) );
403401
TEST( seed==fd_funk_rec_map_seed( rec_map ) );
404402

405-
uint rec_head_idx = funk->rec_head_idx;
406-
uint rec_tail_idx = funk->rec_tail_idx;
407-
408-
int null_rec_head = fd_funk_rec_idx_is_null( rec_head_idx );
409-
int null_rec_tail = fd_funk_rec_idx_is_null( rec_tail_idx );
410-
411-
if( !rec_max ) TEST( null_rec_head & null_rec_tail );
412-
else {
413-
if( null_rec_head ) TEST( null_rec_tail );
414-
else TEST( rec_head_idx<rec_max );
415-
416-
if( null_rec_tail ) TEST( null_rec_head );
417-
else TEST( rec_tail_idx<rec_max );
418-
}
419-
420-
if( !rec_max ) TEST( fd_funk_rec_idx_is_null( rec_tail_idx ) );
421-
422403
TEST( !fd_funk_rec_verify( join ) );
423404

424405
/* Test values */

src/funk/fd_funk.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,6 @@ struct __attribute__((aligned(FD_FUNK_ALIGN))) fd_funk_shmem_private {
239239
rec_max==fd_funk_rec_map_key_max(rec_map) */
240240
ulong rec_pool_gaddr;
241241
ulong rec_ele_gaddr;
242-
uint rec_head_idx; /* Record map index of the first record, FD_FUNK_REC_IDX_NULL if none (from oldest to youngest) */
243-
uint rec_tail_idx; /* " last " */
244242

245243
/* The funk alloc is used for allocating wksp resources for record
246244
values. This is a fd_alloc and more details are given in

src/funk/fd_funk_rec.c

Lines changed: 45 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "fd_funk.h"
2+
#include "fd_funk_txn.h"
23

34
/* Provide the actual record map implementation */
45

@@ -222,6 +223,7 @@ fd_funk_rec_prepare( fd_funk_t * funk,
222223
return NULL;
223224
}
224225
#endif
226+
memset( prepare, 0, sizeof(fd_funk_rec_prepare_t) );
225227

226228
if( !txn ) { /* Modifying last published */
227229
if( FD_UNLIKELY( fd_funk_last_publish_is_frozen( funk ) ) ) {
@@ -239,30 +241,28 @@ fd_funk_rec_prepare( fd_funk_t * funk,
239241
if( opt_err && *opt_err == FD_POOL_ERR_CORRUPT ) {
240242
FD_LOG_ERR(( "corrupt element returned from funk rec pool" ));
241243
}
242-
243-
if( rec != NULL ) {
244-
fd_funk_val_init( rec );
245-
if( txn == NULL ) {
246-
fd_funk_txn_xid_set_root( rec->pair.xid );
247-
rec->txn_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
248-
prepare->rec_head_idx = &funk->shmem->rec_head_idx;
249-
prepare->rec_tail_idx = &funk->shmem->rec_tail_idx;
250-
prepare->txn_lock = &funk->shmem->lock;
251-
} else {
252-
fd_funk_txn_xid_copy( rec->pair.xid, &txn->xid );
253-
rec->txn_cidx = fd_funk_txn_cidx( (ulong)( txn - funk->txn_pool->ele ) );
254-
prepare->rec_head_idx = &txn->rec_head_idx;
255-
prepare->rec_tail_idx = &txn->rec_tail_idx;
256-
prepare->txn_lock = &txn->lock;
257-
}
258-
fd_funk_rec_key_copy( rec->pair.key, key );
259-
rec->tag = 0;
260-
rec->flags = 0;
261-
rec->prev_idx = FD_FUNK_REC_IDX_NULL;
262-
rec->next_idx = FD_FUNK_REC_IDX_NULL;
263-
} else {
244+
if( FD_UNLIKELY( !rec ) ) {
264245
fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_REC );
246+
return rec;
265247
}
248+
249+
fd_funk_val_init( rec );
250+
if( txn == NULL ) {
251+
fd_funk_txn_xid_set_root( rec->pair.xid );
252+
rec->txn_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
253+
prepare->txn_lock = &funk->shmem->lock;
254+
} else {
255+
fd_funk_txn_xid_copy( rec->pair.xid, &txn->xid );
256+
rec->txn_cidx = fd_funk_txn_cidx( (ulong)( txn - funk->txn_pool->ele ) );
257+
prepare->rec_head_idx = &txn->rec_head_idx;
258+
prepare->rec_tail_idx = &txn->rec_tail_idx;
259+
prepare->txn_lock = &txn->lock;
260+
}
261+
fd_funk_rec_key_copy( rec->pair.key, key );
262+
rec->tag = 0;
263+
rec->flags = 0;
264+
rec->prev_idx = FD_FUNK_REC_IDX_NULL;
265+
rec->next_idx = FD_FUNK_REC_IDX_NULL;
266266
return rec;
267267
}
268268

@@ -276,16 +276,17 @@ fd_funk_rec_publish( fd_funk_t * funk,
276276
/* Lock the txn */
277277
while( FD_ATOMIC_CAS( prepare->txn_lock, 0, 1 ) ) FD_SPIN_PAUSE();
278278

279-
uint rec_prev_idx;
280-
uint rec_idx = (uint)( rec - funk->rec_pool->ele );
281-
rec_prev_idx = *rec_tail_idx;
282-
*rec_tail_idx = rec_idx;
283-
rec->prev_idx = rec_prev_idx;
284-
rec->next_idx = FD_FUNK_REC_IDX_NULL;
285-
if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
286-
*rec_head_idx = rec_idx;
287-
} else {
288-
funk->rec_pool->ele[ rec_prev_idx ].next_idx = rec_idx;
279+
if( rec_head_idx ) {
280+
uint rec_idx = (uint)( rec - funk->rec_pool->ele );
281+
uint rec_prev_idx = *rec_tail_idx;
282+
*rec_tail_idx = rec_idx;
283+
rec->prev_idx = rec_prev_idx;
284+
rec->next_idx = FD_FUNK_REC_IDX_NULL;
285+
if( fd_funk_rec_idx_is_null( rec_prev_idx ) ) {
286+
*rec_head_idx = rec_idx;
287+
} else {
288+
funk->rec_pool->ele[ rec_prev_idx ].next_idx = rec_idx;
289+
}
289290
}
290291

291292
if( fd_funk_rec_map_insert( funk->rec_map, rec, FD_MAP_FLAG_BLOCKING ) ) {
@@ -601,7 +602,6 @@ fd_funk_rec_purify( fd_funk_t * funk ) {
601602
fd_funk_rec_map_reset( rec_map );
602603
rec_pool->pool->ver_top = fd_funk_rec_pool_idx_null();;
603604

604-
uint prev_idx = FD_FUNK_REC_IDX_NULL;
605605
for( ulong i = 0; i < rec_max; ++i ) {
606606
fd_funk_rec_t * rec = rec_pool->ele + i;
607607
if( fd_funk_rec_pool_is_in_pool( rec ) ||
@@ -619,21 +619,6 @@ fd_funk_rec_purify( fd_funk_t * funk ) {
619619
rec_used_cnt++;
620620

621621
fd_funk_rec_map_insert( rec_map, rec, FD_MAP_FLAG_BLOCKING );
622-
623-
rec->prev_idx = prev_idx;
624-
if( prev_idx != FD_FUNK_REC_IDX_NULL ) {
625-
(rec_pool->ele + prev_idx)->next_idx = fd_funk_rec_map_private_cidx( i );
626-
} else {
627-
funk->shmem->rec_head_idx = fd_funk_rec_map_private_cidx( i );
628-
}
629-
prev_idx = fd_funk_rec_map_private_cidx( i );
630-
}
631-
632-
funk->shmem->rec_tail_idx = prev_idx;
633-
if( prev_idx != FD_FUNK_REC_IDX_NULL ) {
634-
(rec_pool->ele + prev_idx)->next_idx = FD_FUNK_REC_IDX_NULL;
635-
} else {
636-
funk->shmem->rec_head_idx = FD_FUNK_REC_IDX_NULL;
637622
}
638623

639624
FD_LOG_NOTICE(( "funk records used after purify: %u, free: %u", rec_used_cnt, rec_free_cnt ));
@@ -675,6 +660,9 @@ fd_funk_rec_verify( fd_funk_t * funk ) {
675660
if( fd_funk_txn_idx_is_null( txn_idx ) ) { /* This is a record from the last published transaction */
676661

677662
TEST( fd_funk_txn_xid_eq_root( txn_xid ) );
663+
/* No record linked list at the root txn */
664+
TEST( fd_funk_rec_idx_is_null( rec->prev_idx ) );
665+
TEST( fd_funk_rec_idx_is_null( rec->next_idx ) );
678666

679667
} else { /* This is a record from an in-prep transaction */
680668

@@ -687,26 +675,20 @@ fd_funk_rec_verify( fd_funk_t * funk ) {
687675
}
688676
}
689677

690-
/* Clear record tags and then verify the forward and reverse linkage */
678+
/* Clear record tags and then verify membership */
691679

692680
for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool->ele[ rec_idx ].tag = 0U;
693681

694682
do {
695-
ulong txn_idx = FD_FUNK_TXN_IDX_NULL;
696-
uint rec_idx = funk->shmem->rec_head_idx;
697-
while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
698-
TEST( (rec_idx<rec_max) && (fd_funk_txn_idx( rec_pool->ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool->ele[ rec_idx ].tag==0U );
699-
rec_pool->ele[ rec_idx ].tag = 1U;
700-
fd_funk_rec_query_t query[1];
701-
fd_funk_rec_t const * rec2 = fd_funk_rec_query_try_global( funk, NULL, rec_pool->ele[ rec_idx ].pair.key, NULL, query );
702-
if( FD_UNLIKELY( rec_pool->ele[ rec_idx ].flags & FD_FUNK_REC_FLAG_ERASE ) )
703-
TEST( rec2 == NULL );
704-
else
705-
TEST( rec2 == rec_pool->ele + rec_idx );
706-
uint next_idx = rec_pool->ele[ rec_idx ].next_idx;
707-
if( !fd_funk_rec_idx_is_null( next_idx ) ) TEST( rec_pool->ele[ next_idx ].prev_idx==rec_idx );
708-
rec_idx = next_idx;
683+
fd_funk_all_iter_t iter[1];
684+
for( fd_funk_all_iter_new( funk, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
685+
fd_funk_rec_t * rec = fd_funk_all_iter_ele( iter );
686+
if( fd_funk_txn_xid_eq_root( rec->pair.xid ) ) {
687+
TEST( rec->tag==0U );
688+
rec->tag = 1U;
689+
}
709690
}
691+
710692
fd_funk_txn_all_iter_t txn_iter[1];
711693
for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
712694
fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
@@ -735,16 +717,6 @@ fd_funk_rec_verify( fd_funk_t * funk ) {
735717
}
736718

737719
do {
738-
ulong txn_idx = FD_FUNK_TXN_IDX_NULL;
739-
uint rec_idx = funk->shmem->rec_tail_idx;
740-
while( !fd_funk_rec_idx_is_null( rec_idx ) ) {
741-
TEST( (rec_idx<rec_max) && (fd_funk_txn_idx( rec_pool->ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool->ele[ rec_idx ].tag==1U );
742-
rec_pool->ele[ rec_idx ].tag = 2U;
743-
uint prev_idx = rec_pool->ele[ rec_idx ].prev_idx;
744-
if( !fd_funk_rec_idx_is_null( prev_idx ) ) TEST( rec_pool->ele[ prev_idx ].next_idx==rec_idx );
745-
rec_idx = prev_idx;
746-
}
747-
748720
fd_funk_txn_all_iter_t txn_iter[1];
749721
for( fd_funk_txn_all_iter_new( funk, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
750722
fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );

src/funk/fd_funk_txn.c

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -478,23 +478,23 @@ fd_funk_txn_cancel_all( fd_funk_t * funk,
478478
changing the order of existing values. */
479479

480480
static void
481-
fd_funk_txn_update( fd_funk_t * funk,
482-
uint * _dst_rec_head_idx, /* Pointer to the dst list head */
483-
uint * _dst_rec_tail_idx, /* Pointer to the dst list tail */
484-
ulong dst_txn_idx, /* Transaction index of the merge destination */
485-
fd_funk_txn_xid_t const * dst_xid, /* dst xid */
486-
ulong txn_idx ) { /* Transaction index of the records to merge */
481+
fd_funk_txn_update( fd_funk_t * funk,
482+
uint * _dst_rec_head_idx, /* Pointer to the dst list head */
483+
uint * _dst_rec_tail_idx, /* Pointer to the dst list tail */
484+
ulong dst_txn_idx, /* Transaction index of the merge destination */
485+
fd_funk_txn_xid_t const * dst_xid, /* dst xid */
486+
ulong txn_idx ) { /* Transaction index of the records to merge */
487487
fd_wksp_t * wksp = funk->wksp;
488488
fd_alloc_t * alloc = funk->alloc;
489489
fd_funk_rec_map_t * rec_map = funk->rec_map;
490490
fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
491491
fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
492492

493-
int critical = (_dst_rec_head_idx == &funk->shmem->rec_head_idx);
494-
if (critical) {
493+
int const critical = _dst_rec_head_idx==NULL;
494+
if( critical ) {
495495
/* If the root transaction is being updated, we need to mark
496496
the beginning of a critical section becuase fd_funk_purify can't fix it */
497-
fd_begin_crit(funk);
497+
fd_begin_crit( funk );
498498
}
499499

500500
fd_funk_txn_t * txn = &txn_pool->ele[ txn_idx ];
@@ -518,12 +518,12 @@ fd_funk_txn_update( fd_funk_t * funk,
518518
uint prev_idx = rec2->prev_idx;
519519
uint next_idx = rec2->next_idx;
520520
if( fd_funk_rec_idx_is_null( prev_idx ) ) {
521-
*_dst_rec_head_idx = next_idx;
521+
if( _dst_rec_head_idx ) *_dst_rec_head_idx = next_idx;
522522
} else {
523523
rec_pool->ele[ prev_idx ].next_idx = next_idx;
524524
}
525525
if( fd_funk_rec_idx_is_null( next_idx ) ) {
526-
*_dst_rec_tail_idx = prev_idx;
526+
if( _dst_rec_tail_idx ) *_dst_rec_tail_idx = prev_idx;
527527
} else {
528528
rec_pool->ele[ next_idx ].prev_idx = prev_idx;
529529
}
@@ -544,14 +544,16 @@ fd_funk_txn_update( fd_funk_t * funk,
544544
rec->pair.xid[0] = *dst_xid;
545545
rec->txn_cidx = fd_funk_txn_cidx( dst_txn_idx );
546546

547-
if( fd_funk_rec_idx_is_null( *_dst_rec_head_idx ) ) {
548-
*_dst_rec_head_idx = rec_idx;
549-
rec->prev_idx = FD_FUNK_REC_IDX_NULL;
550-
} else {
551-
rec_pool->ele[ *_dst_rec_tail_idx ].next_idx = rec_idx;
552-
rec->prev_idx = *_dst_rec_tail_idx;
547+
rec->prev_idx = FD_FUNK_REC_IDX_NULL;
548+
if( _dst_rec_head_idx ) {
549+
if( fd_funk_rec_idx_is_null( *_dst_rec_head_idx ) ) {
550+
*_dst_rec_head_idx = rec_idx;
551+
} else {
552+
rec_pool->ele[ *_dst_rec_tail_idx ].next_idx = rec_idx;
553+
rec->prev_idx = *_dst_rec_tail_idx;
554+
}
555+
*_dst_rec_tail_idx = rec_idx;
553556
}
554-
*_dst_rec_tail_idx = rec_idx;
555557
rec->next_idx = FD_FUNK_REC_IDX_NULL;
556558

557559
rec_idx = next_rec_idx;
@@ -560,8 +562,8 @@ fd_funk_txn_update( fd_funk_t * funk,
560562
txn_pool->ele[ txn_idx ].rec_head_idx = FD_FUNK_REC_IDX_NULL;
561563
txn_pool->ele[ txn_idx ].rec_tail_idx = FD_FUNK_REC_IDX_NULL;
562564

563-
if (critical) {
564-
fd_end_crit(funk);
565+
if( critical ) {
566+
fd_end_crit( funk );
565567
}
566568
}
567569

@@ -578,7 +580,7 @@ fd_funk_txn_publish_funk_child( fd_funk_t * const funk,
578580

579581
/* Apply the updates in txn to the last published transactions */
580582

581-
fd_funk_txn_update( funk, &funk->shmem->rec_head_idx, &funk->shmem->rec_tail_idx, FD_FUNK_TXN_IDX_NULL, fd_funk_root( funk ), txn_idx );
583+
fd_funk_txn_update( funk, NULL, NULL, FD_FUNK_TXN_IDX_NULL, fd_funk_root( funk ), txn_idx );
582584

583585
/* Cancel all competing transaction histories */
584586

@@ -706,7 +708,7 @@ fd_funk_txn_publish_into_parent( fd_funk_t * funk,
706708
ulong parent_idx = fd_funk_txn_idx( txn->parent_cidx );
707709
if( fd_funk_txn_idx_is_null( parent_idx ) ) {
708710
/* Publish to root */
709-
fd_funk_txn_update( funk, &funk->shmem->rec_head_idx, &funk->shmem->rec_tail_idx, FD_FUNK_TXN_IDX_NULL, fd_funk_root( funk ), txn_idx );
711+
fd_funk_txn_update( funk, NULL, NULL, FD_FUNK_TXN_IDX_NULL, fd_funk_root( funk ), txn_idx );
710712
/* Inherit the children */
711713
funk->shmem->child_head_cidx = txn->child_head_cidx;
712714
funk->shmem->child_tail_cidx = txn->child_tail_cidx;
@@ -733,29 +735,20 @@ fd_funk_txn_publish_into_parent( fd_funk_t * funk,
733735
return FD_FUNK_SUCCESS;
734736
}
735737

736-
/* Return the first record in a transaction. Returns NULL if the
737-
transaction has no records yet. */
738-
739738
fd_funk_rec_t const *
740739
fd_funk_txn_first_rec( fd_funk_t * funk,
741740
fd_funk_txn_t const * txn ) {
742-
uint rec_idx;
743-
if( FD_UNLIKELY( NULL == txn ))
744-
rec_idx = funk->shmem->rec_head_idx;
745-
else
746-
rec_idx = txn->rec_head_idx;
741+
if( FD_UNLIKELY( !txn ) ) return NULL;
742+
uint rec_idx = txn->rec_head_idx;
747743
if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL;
748744
return funk->rec_pool->ele + rec_idx;
749745
}
750746

751747
fd_funk_rec_t const *
752748
fd_funk_txn_last_rec( fd_funk_t * funk,
753749
fd_funk_txn_t const * txn ) {
754-
uint rec_idx;
755-
if( FD_UNLIKELY( NULL == txn ))
756-
rec_idx = funk->shmem->rec_tail_idx;
757-
else
758-
rec_idx = txn->rec_tail_idx;
750+
if( FD_UNLIKELY( !txn ) ) return NULL;
751+
uint rec_idx = txn->rec_tail_idx;
759752
if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL;
760753
return funk->rec_pool->ele + rec_idx;
761754
}

src/funk/fd_funk_txn.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,14 @@ fd_funk_txn_is_only_child( fd_funk_txn_t const * txn ) {
224224

225225
typedef struct fd_funk_rec fd_funk_rec_t;
226226

227-
/* Return the first (oldest) record in a transaction. Returns NULL if the
228-
transaction has no records yet. */
227+
/* fd_funk_txn_{first,last}_rec return the {first,last} record in a
228+
transaction. Returns NULL if the transaction has no records yet, or
229+
if txn is NULL (root txn). */
229230

230231
fd_funk_rec_t const *
231232
fd_funk_txn_first_rec( fd_funk_t * funk,
232233
fd_funk_txn_t const * txn );
233234

234-
/* Return the last (newest) record in a transaction. Returns NULL if the
235-
transaction has no records yet. */
236-
237235
fd_funk_rec_t const *
238236
fd_funk_txn_last_rec( fd_funk_t * funk,
239237
fd_funk_txn_t const * txn );

0 commit comments

Comments
 (0)