Skip to content

Commit

Permalink
BeaconState: remaining shared reference fields with conditional copy …
Browse files Browse the repository at this point in the history
…on write (#4785)

* The remaining shared reference fields with conditional copy on write
* Merge branch 'master' into better-copy-2
  • Loading branch information
prestonvanloon committed Feb 6, 2020
1 parent a9d144a commit f6dfaef
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 21 deletions.
115 changes: 107 additions & 8 deletions beacon-chain/state/setters.go
Expand Up @@ -174,6 +174,9 @@ func (b *BeaconState) SetHistoricalRoots(val [][]byte) error {
b.lock.Lock()
defer b.lock.Unlock()

b.sharedFieldReferences[historicalRoots].refs--
b.sharedFieldReferences[historicalRoots] = &reference{refs: 1}

b.state.HistoricalRoots = val
b.markFieldAsDirty(historicalRoots)
return nil
Expand All @@ -195,6 +198,9 @@ func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
b.lock.Lock()
defer b.lock.Unlock()

b.sharedFieldReferences[eth1DataVotes].refs--
b.sharedFieldReferences[eth1DataVotes] = &reference{refs: 1}

b.state.Eth1DataVotes = val
b.markFieldAsDirty(eth1DataVotes)
return nil
Expand All @@ -203,10 +209,19 @@ func (b *BeaconState) SetEth1DataVotes(val []*ethpb.Eth1Data) error {
// AppendEth1DataVotes for the beacon state. This PR appends the new value
// to the the end of list.
func (b *BeaconState) AppendEth1DataVotes(val *ethpb.Eth1Data) error {
b.lock.RLock()
votes := b.state.Eth1DataVotes
if b.sharedFieldReferences[eth1DataVotes].refs > 1 {
votes = b.Eth1DataVotes()
b.sharedFieldReferences[eth1DataVotes].refs--
b.sharedFieldReferences[eth1DataVotes] = &reference{refs: 1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.Eth1DataVotes = append(b.state.Eth1DataVotes, val)
b.state.Eth1DataVotes = append(votes, val)
b.markFieldAsDirty(eth1DataVotes)
return nil
}
Expand Down Expand Up @@ -309,6 +324,9 @@ func (b *BeaconState) SetBalances(val []uint64) error {
b.lock.Lock()
defer b.lock.Unlock()

b.sharedFieldReferences[balances].refs--
b.sharedFieldReferences[balances] = &reference{refs:1}

b.state.Balances = val
b.markFieldAsDirty(balances)
return nil
Expand All @@ -320,10 +338,21 @@ func (b *BeaconState) UpdateBalancesAtIndex(idx uint64, val uint64) error {
if len(b.state.Balances) <= int(idx) {
return errors.Errorf("invalid index provided %d", idx)
}

b.lock.RLock()
bals := b.state.Balances
if b.sharedFieldReferences[balances].refs > 1 {
bals = b.Balances()
b.sharedFieldReferences[balances].refs--
b.sharedFieldReferences[balances] = &reference{refs: 1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.Balances[idx] = val
bals[idx] = val
b.state.Balances = bals
b.markFieldAsDirty(balances)
return nil
}
Expand Down Expand Up @@ -373,6 +402,9 @@ func (b *BeaconState) SetSlashings(val []uint64) error {
b.lock.Lock()
defer b.lock.Unlock()

b.sharedFieldReferences[slashings].refs--
b.sharedFieldReferences[slashings] = &reference{refs: 1}

b.state.Slashings = val
b.markFieldAsDirty(slashings)
return nil
Expand All @@ -384,10 +416,23 @@ func (b *BeaconState) UpdateSlashingsAtIndex(idx uint64, val uint64) error {
if len(b.state.Slashings) <= int(idx) {
return errors.Errorf("invalid index provided %d", idx)
}
b.lock.RLock()
s := b.state.Slashings

if b.sharedFieldReferences[slashings].refs > 1 {
s = b.Slashings()
b.sharedFieldReferences[slashings].refs--
b.sharedFieldReferences[slashings] = &reference{refs: 1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.Slashings[idx] = val
s[idx] = val

b.state.Slashings = s

b.markFieldAsDirty(slashings)
return nil
}
Expand All @@ -398,6 +443,9 @@ func (b *BeaconState) SetPreviousEpochAttestations(val []*pbp2p.PendingAttestati
b.lock.Lock()
defer b.lock.Unlock()

b.sharedFieldReferences[previousEpochAttestations].refs--
b.sharedFieldReferences[previousEpochAttestations] = &reference{refs: 1}

b.state.PreviousEpochAttestations = val
b.markFieldAsDirty(previousEpochAttestations)
return nil
Expand All @@ -409,6 +457,10 @@ func (b *BeaconState) SetCurrentEpochAttestations(val []*pbp2p.PendingAttestatio
b.lock.Lock()
defer b.lock.Unlock()


b.sharedFieldReferences[currentEpochAttestations].refs--
b.sharedFieldReferences[currentEpochAttestations] = &reference{refs: 1}

b.state.CurrentEpochAttestations = val
b.markFieldAsDirty(currentEpochAttestations)
return nil
Expand All @@ -417,54 +469,101 @@ func (b *BeaconState) SetCurrentEpochAttestations(val []*pbp2p.PendingAttestatio
// AppendHistoricalRoots for the beacon state. This PR appends the new value
// to the the end of list.
func (b *BeaconState) AppendHistoricalRoots(root [32]byte) error {
b.lock.RLock()
roots := b.state.HistoricalRoots
if b.sharedFieldReferences[historicalRoots].refs > 1 {
roots = b.HistoricalRoots()
b.sharedFieldReferences[historicalRoots].refs--
b.sharedFieldReferences[historicalRoots] = &reference{refs: 1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.HistoricalRoots = append(b.state.HistoricalRoots, root[:])
b.state.HistoricalRoots = append(roots, root[:])
b.markFieldAsDirty(historicalRoots)
return nil
}

// AppendCurrentEpochAttestations for the beacon state. This PR appends the new value
// to the the end of list.
func (b *BeaconState) AppendCurrentEpochAttestations(val *pbp2p.PendingAttestation) error {
b.lock.RLock()

atts := b.state.CurrentEpochAttestations
if b.sharedFieldReferences[currentEpochAttestations].refs > 1 {
atts = b.CurrentEpochAttestations()
b.sharedFieldReferences[currentEpochAttestations].refs--
b.sharedFieldReferences[currentEpochAttestations] = &reference{refs: 1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.CurrentEpochAttestations = append(b.state.CurrentEpochAttestations, val)
b.state.CurrentEpochAttestations = append(atts, val)
b.markFieldAsDirty(currentEpochAttestations)
return nil
}

// AppendPreviousEpochAttestations for the beacon state. This PR appends the new value
// to the the end of list.
func (b *BeaconState) AppendPreviousEpochAttestations(val *pbp2p.PendingAttestation) error {
b.lock.RLock()
atts := b.state.PreviousEpochAttestations
if b.sharedFieldReferences[previousEpochAttestations].refs > 1 {
atts = b.PreviousEpochAttestations()
b.sharedFieldReferences[previousEpochAttestations].refs--
b.sharedFieldReferences[previousEpochAttestations] = &reference{refs:1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.PreviousEpochAttestations = append(b.state.PreviousEpochAttestations, val)
b.state.PreviousEpochAttestations = append(atts, val)
b.markFieldAsDirty(previousEpochAttestations)
return nil
}

// AppendValidator for the beacon state. This PR appends the new value
// to the the end of list.
func (b *BeaconState) AppendValidator(val *ethpb.Validator) error {
b.lock.RLock()
vals := b.state.Validators
if b.sharedFieldReferences[validators].refs > 1 {
vals = b.Validators()
b.sharedFieldReferences[validators].refs--
b.sharedFieldReferences[validators] = &reference{refs:1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.Validators = append(b.state.Validators, val)
b.state.Validators = append(vals, val)
b.markFieldAsDirty(validators)
return nil
}

// AppendBalance for the beacon state. This PR appends the new value
// to the the end of list.
func (b *BeaconState) AppendBalance(bal uint64) error {
b.lock.RLock()

bals := b.state.Balances
if b.sharedFieldReferences[balances].refs > 1 {
bals = b.Balances()
b.sharedFieldReferences[balances].refs--
b.sharedFieldReferences[balances] = &reference{refs:1}
}
b.lock.RUnlock()

b.lock.Lock()
defer b.lock.Unlock()

b.state.Balances = append(b.state.Balances, bal)
b.state.Balances = append(bals, bal)
b.markFieldAsDirty(balances)
return nil
}
Expand Down
31 changes: 18 additions & 13 deletions beacon-chain/state/types.go
Expand Up @@ -54,7 +54,7 @@ func InitializeFromProtoUnsafe(st *pbp2p.BeaconState) (*BeaconState, error) {
b := &BeaconState{
state: st,
dirtyFields: make(map[fieldIndex]interface{}, 20),
sharedFieldReferences: make(map[fieldIndex]*reference, 4),
sharedFieldReferences: make(map[fieldIndex]*reference, 10),
valIdxMap: coreutils.ValidatorIndexMap(st.Validators),
}

Expand All @@ -66,7 +66,13 @@ func InitializeFromProtoUnsafe(st *pbp2p.BeaconState) (*BeaconState, error) {
b.sharedFieldReferences[randaoMixes] = &reference{refs: 1}
b.sharedFieldReferences[stateRoots] = &reference{refs: 1}
b.sharedFieldReferences[blockRoots] = &reference{refs: 1}
b.sharedFieldReferences[previousEpochAttestations] = &reference{refs: 1}
b.sharedFieldReferences[currentEpochAttestations] = &reference{refs: 1}
b.sharedFieldReferences[slashings] = &reference{refs: 1}
b.sharedFieldReferences[eth1DataVotes] = &reference{refs: 1}
b.sharedFieldReferences[validators] = &reference{refs: 1}
b.sharedFieldReferences[balances] = &reference{refs: 1}
b.sharedFieldReferences[historicalRoots] = &reference{refs: 1}

return b, nil
}
Expand All @@ -84,20 +90,19 @@ func (b *BeaconState) Copy() *BeaconState {
Eth1DepositIndex: b.state.Eth1DepositIndex,

// Large arrays, infrequently changed, constant size.
RandaoMixes: b.state.RandaoMixes,
StateRoots: b.state.StateRoots,
BlockRoots: b.state.BlockRoots,
RandaoMixes: b.state.RandaoMixes,
StateRoots: b.state.StateRoots,
BlockRoots: b.state.BlockRoots,
PreviousEpochAttestations: b.state.PreviousEpochAttestations,
CurrentEpochAttestations: b.state.CurrentEpochAttestations,
Slashings: b.state.Slashings,
Eth1DataVotes: b.state.Eth1DataVotes,

// Large arrays, increases over time.
Validators: b.state.Validators,
Validators: b.state.Validators,
Balances: b.state.Balances,
HistoricalRoots: b.state.HistoricalRoots,

// Potential candidates for copy-on-write.
Balances: b.Balances(),
HistoricalRoots: b.HistoricalRoots(),
PreviousEpochAttestations: b.PreviousEpochAttestations(),
CurrentEpochAttestations: b.CurrentEpochAttestations(),
Slashings: b.Slashings(),
Eth1DataVotes: b.Eth1DataVotes(),

// Everything else, too small to be concerned about, constant size.
Fork: b.Fork(),
Expand All @@ -109,7 +114,7 @@ func (b *BeaconState) Copy() *BeaconState {
FinalizedCheckpoint: b.FinalizedCheckpoint(),
},
dirtyFields: make(map[fieldIndex]interface{}, 20),
sharedFieldReferences: make(map[fieldIndex]*reference, 4),
sharedFieldReferences: make(map[fieldIndex]*reference, 10),

// Copy on write validator index map.
valIdxMap: b.valIdxMap,
Expand Down

0 comments on commit f6dfaef

Please sign in to comment.