-
Notifications
You must be signed in to change notification settings - Fork 261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
avoid memory allocations and copies when loading states #937
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ import | |
attestation_pool, block_pool, eth2_network, eth2_discovery, | ||
beacon_node_types, mainchain_monitor, version, ssz, ssz/dynamic_navigator, | ||
sync_protocol, request_manager, validator_keygen, interop, statusbar, | ||
attestation_aggregation, sync_manager | ||
attestation_aggregation, sync_manager, state_transition | ||
|
||
const | ||
genesisFile = "genesis.ssz" | ||
|
@@ -121,8 +121,8 @@ proc getStateFromSnapshot(conf: BeaconNodeConf): NilableBeaconStateRef = | |
error "Failed to read genesis file", err = err.msg | ||
quit 1 | ||
|
||
try: | ||
result = SSZ.decode(snapshotContents, BeaconStateRef) | ||
result = try: | ||
newClone(SSZ.decode(snapshotContents, BeaconState)) | ||
except SerializationError: | ||
error "Failed to import genesis file", path = genesisPath | ||
quit 1 | ||
|
@@ -192,7 +192,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async | |
let tailBlock = get_initial_beacon_block(genesisState[]) | ||
|
||
try: | ||
BlockPool.preInit(db, genesisState, tailBlock) | ||
BlockPool.preInit(db, genesisState[], tailBlock) | ||
doAssert BlockPool.isInitialized(db), "preInit should have initialized db" | ||
except CatchableError as e: | ||
error "Failed to initialize database", err = e.msg | ||
|
@@ -219,7 +219,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async | |
nil | ||
|
||
let | ||
enrForkId = enrForkIdFromState(blockPool.headState.data.data[]) | ||
enrForkId = enrForkIdFromState(blockPool.headState.data.data) | ||
topicBeaconBlocks = getBeaconBlocksTopic(enrForkId.forkDigest) | ||
topicAggregateAndProofs = getAggregateAndProofsTopic(enrForkId.forkDigest) | ||
network = await createEth2Node(conf, enrForkId) | ||
|
@@ -235,7 +235,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async | |
blockPool: blockPool, | ||
attestationPool: AttestationPool.init(blockPool), | ||
mainchainMonitor: mainchainMonitor, | ||
beaconClock: BeaconClock.init(blockPool.headState.data.data[]), | ||
beaconClock: BeaconClock.init(blockPool.headState.data.data), | ||
rpcServer: rpcServer, | ||
forkDigest: enrForkId.forkDigest, | ||
topicBeaconBlocks: topicBeaconBlocks, | ||
|
@@ -410,15 +410,15 @@ proc proposeBlock(node: BeaconNode, | |
(get_eth1data_stub(state.eth1_deposit_index, slot.compute_epoch_at_slot()), | ||
newSeq[Deposit]()) | ||
else: | ||
node.mainchainMonitor.getBlockProposalData(state[]) | ||
node.mainchainMonitor.getBlockProposalData(state) | ||
|
||
let message = makeBeaconBlock( | ||
state[], | ||
state, | ||
head.root, | ||
validator.genRandaoReveal(state.fork, state.genesis_validators_root, slot), | ||
eth1data, | ||
Eth2Digest(), | ||
node.attestationPool.getAttestationsForBlock(state[]), | ||
node.attestationPool.getAttestationsForBlock(state), | ||
deposits) | ||
|
||
if not message.isSome(): | ||
|
@@ -546,7 +546,7 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) = | |
slot = shortLog(slot) | ||
return | ||
|
||
let attestationHead = head.findAncestorBySlot(slot) | ||
let attestationHead = head.atSlot(slot) | ||
if head != attestationHead.blck: | ||
# In rare cases, such as when we're busy syncing or just slow, we'll be | ||
# attesting to a past state - we must then recreate the world as it looked | ||
|
@@ -576,16 +576,16 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) = | |
# version here that calculates the committee for a single slot only | ||
node.blockPool.withState(node.blockPool.tmpState, attestationHead): | ||
var cache = get_empty_per_epoch_cache() | ||
let committees_per_slot = get_committee_count_at_slot(state[], slot) | ||
let committees_per_slot = get_committee_count_at_slot(state, slot) | ||
|
||
for committee_index in 0'u64..<committees_per_slot: | ||
let committee = get_beacon_committee( | ||
state[], slot, committee_index.CommitteeIndex, cache) | ||
state, slot, committee_index.CommitteeIndex, cache) | ||
|
||
for index_in_committee, validatorIdx in committee: | ||
let validator = node.getAttachedValidator(state[], validatorIdx) | ||
let validator = node.getAttachedValidator(state, validatorIdx) | ||
if validator != nil: | ||
let ad = makeAttestationData(state[], slot, committee_index, blck.root) | ||
let ad = makeAttestationData(state, slot, committee_index, blck.root) | ||
attestations.add((ad, committee.len, index_in_committee, validator)) | ||
|
||
for a in attestations: | ||
|
@@ -649,21 +649,21 @@ proc broadcastAggregatedAttestations( | |
let bs = BlockSlot(blck: aggregationHead, slot: aggregationSlot) | ||
node.blockPool.withState(node.blockPool.tmpState, bs): | ||
let | ||
committees_per_slot = get_committee_count_at_slot(state[], aggregationSlot) | ||
committees_per_slot = get_committee_count_at_slot(state, aggregationSlot) | ||
var cache = get_empty_per_epoch_cache() | ||
for committee_index in 0'u64..<committees_per_slot: | ||
let committee = get_beacon_committee( | ||
state[], aggregationSlot, committee_index.CommitteeIndex, cache) | ||
state, aggregationSlot, committee_index.CommitteeIndex, cache) | ||
|
||
for index_in_committee, validatorIdx in committee: | ||
let validator = node.getAttachedValidator(state[], validatorIdx) | ||
let validator = node.getAttachedValidator(state, validatorIdx) | ||
if validator != nil: | ||
# This is slightly strange/inverted control flow, since really it's | ||
# going to happen once per slot, but this is the best way to get at | ||
# the validator index and private key pair. TODO verify it only has | ||
# one isSome() with test. | ||
let aggregateAndProof = | ||
aggregate_attestations(node.attestationPool, state[], | ||
aggregate_attestations(node.attestationPool, state, | ||
committee_index.CommitteeIndex, | ||
# TODO https://github.com/status-im/nim-beacon-chain/issues/545 | ||
# this assumes in-process private keys | ||
|
@@ -1060,13 +1060,13 @@ proc installBeaconApiHandlers(rpcServer: RpcServer, node: BeaconNode) = | |
requireOneOf(slot, root) | ||
if slot.isSome: | ||
let blk = node.blockPool.head.blck.atSlot(slot.get) | ||
var tmpState = emptyStateData() | ||
node.blockPool.withState(tmpState, blk): | ||
node.blockPool.withState(node.blockPool.tmpState, blk): | ||
return jsonResult(state) | ||
else: | ||
let state = node.db.getState(root.get) | ||
if state.isSome: | ||
return jsonResult(state.get) | ||
let tmp = BeaconStateRef() # TODO use tmpState - but load the entire StateData! | ||
let state = node.db.getState(root.get, tmp[], noRollback) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another usage of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the idea is not to allocate here but rather reuse an existing instance like |
||
if state: | ||
return jsonResult(tmp[]) | ||
else: | ||
return StringOfJson("null") | ||
|
||
|
@@ -1193,7 +1193,7 @@ proc start(node: BeaconNode) = | |
bs = BlockSlot(blck: head.blck, slot: head.blck.slot) | ||
|
||
node.blockPool.withState(node.blockPool.tmpState, bs): | ||
node.addLocalValidators(state[]) | ||
node.addLocalValidators(state) | ||
|
||
node.run() | ||
|
||
|
@@ -1282,7 +1282,7 @@ when hasPrompt: | |
# TODO slow linear scan! | ||
for idx, b in node.blockPool.headState.data.data.balances: | ||
if node.getAttachedValidator( | ||
node.blockPool.headState.data.data[], ValidatorIndex(idx)) != nil: | ||
node.blockPool.headState.data.data, ValidatorIndex(idx)) != nil: | ||
balance += b | ||
formatGwei(balance) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's easy to create a helper that can write to a var directly. I'll look into after the PR is merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the serialization library would have to be rewritten to not use exceptions and assume a blank slate (empty seqs etc) which would complicate it somewhat - on the plus side it could reuse
seq
memory etc instead of allocating new seqs - in general though, it's uglier and less safe - specially in the presence of exceptions - thisBeaconChainDB
acts as an exception barrier so it can do tricks like this somewhat safely as long as it has the rollback workaroundthe bigger issue here is the lifetime of outputAddr - I wish there was a safe construct for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that if BeaconState becomes a ref, the compiler might move it somewhere else on the heap and invalidate the address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, I put
unsafeAddr
here so that we can grep for it easily - it should be safe though since the scope is localThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
besides,
nim
isn't fancy enough to do compacting, is it?