Revert parent->child optimisation AGAIN

This commit is contained in:
Michael Sproul
2026-03-31 15:47:50 +11:00
parent 7570fd1555
commit eceedaf7b6

View File

@@ -1104,26 +1104,6 @@ impl ProtoArray {
Ok((best_fc_node.root, best_fc_node.payload_status)) Ok((best_fc_node.root, best_fc_node.payload_status))
} }
/// Build a parent->children index. Invalid nodes are excluded
/// (they aren't in store.blocks in the spec).
fn build_children_index(&self) -> Vec<Vec<usize>> {
let mut children = vec![vec![]; self.nodes.len()];
for (i, node) in self.nodes.iter().enumerate() {
if node
.execution_status()
.is_ok_and(|status| status.is_invalid())
{
continue;
}
if let Some(parent) = node.parent()
&& parent < children.len()
{
children[parent].push(i);
}
}
children
}
/// Spec: `get_filtered_block_tree`. /// Spec: `get_filtered_block_tree`.
/// ///
/// Returns the set of node indices on viable branches — those with at least /// Returns the set of node indices on viable branches — those with at least
@@ -1134,7 +1114,6 @@ impl ProtoArray {
current_slot: Slot, current_slot: Slot,
best_justified_checkpoint: Checkpoint, best_justified_checkpoint: Checkpoint,
best_finalized_checkpoint: Checkpoint, best_finalized_checkpoint: Checkpoint,
children_index: &[Vec<usize>],
) -> HashSet<usize> { ) -> HashSet<usize> {
let mut viable = HashSet::new(); let mut viable = HashSet::new();
self.filter_block_tree::<E>( self.filter_block_tree::<E>(
@@ -1142,7 +1121,6 @@ impl ProtoArray {
current_slot, current_slot,
best_justified_checkpoint, best_justified_checkpoint,
best_finalized_checkpoint, best_finalized_checkpoint,
children_index,
&mut viable, &mut viable,
); );
viable viable
@@ -1155,17 +1133,25 @@ impl ProtoArray {
current_slot: Slot, current_slot: Slot,
best_justified_checkpoint: Checkpoint, best_justified_checkpoint: Checkpoint,
best_finalized_checkpoint: Checkpoint, best_finalized_checkpoint: Checkpoint,
children_index: &[Vec<usize>],
viable: &mut HashSet<usize>, viable: &mut HashSet<usize>,
) -> bool { ) -> bool {
let Some(node) = self.nodes.get(node_index) else { let Some(node) = self.nodes.get(node_index) else {
return false; return false;
}; };
let children = children_index // Skip invalid children — they aren't in store.blocks in the spec.
.get(node_index) let children: Vec<usize> = self
.map(|c| c.as_slice()) .nodes
.unwrap_or(&[]); .iter()
.enumerate()
.filter(|(_, child)| {
child.parent() == Some(node_index)
&& !child
.execution_status()
.is_ok_and(|status| status.is_invalid())
})
.map(|(i, _)| i)
.collect();
if !children.is_empty() { if !children.is_empty() {
// Evaluate ALL children (no short-circuit) to mark all viable branches. // Evaluate ALL children (no short-circuit) to mark all viable branches.
@@ -1177,7 +1163,6 @@ impl ProtoArray {
current_slot, current_slot,
best_justified_checkpoint, best_justified_checkpoint,
best_finalized_checkpoint, best_finalized_checkpoint,
children_index,
viable, viable,
) )
}) })
@@ -1222,16 +1207,12 @@ impl ProtoArray {
payload_status: PayloadStatus::Pending, payload_status: PayloadStatus::Pending,
}; };
// Build parent->children index once for O(1) lookups.
let children_index = self.build_children_index();
// Spec: `get_filtered_block_tree`. // Spec: `get_filtered_block_tree`.
let viable_nodes = self.get_filtered_block_tree::<E>( let viable_nodes = self.get_filtered_block_tree::<E>(
start_index, start_index,
current_slot, current_slot,
best_justified_checkpoint, best_justified_checkpoint,
best_finalized_checkpoint, best_finalized_checkpoint,
&children_index,
); );
// Compute once rather than per-child per-level. // Compute once rather than per-child per-level.
@@ -1240,7 +1221,7 @@ impl ProtoArray {
loop { loop {
let children: Vec<_> = self let children: Vec<_> = self
.get_node_children(&head, &children_index)? .get_node_children(&head)?
.into_iter() .into_iter()
.filter(|(fc_node, _)| viable_nodes.contains(&fc_node.proto_node_index)) .filter(|(fc_node, _)| viable_nodes.contains(&fc_node.proto_node_index))
.collect(); .collect();
@@ -1403,7 +1384,6 @@ impl ProtoArray {
fn get_node_children( fn get_node_children(
&self, &self,
node: &IndexedForkChoiceNode, node: &IndexedForkChoiceNode,
children_index: &[Vec<usize>],
) -> Result<Vec<(IndexedForkChoiceNode, ProtoNode)>, Error> { ) -> Result<Vec<(IndexedForkChoiceNode, ProtoNode)>, Error> {
if node.payload_status == PayloadStatus::Pending { if node.payload_status == PayloadStatus::Pending {
let proto_node = self let proto_node = self
@@ -1417,25 +1397,23 @@ impl ProtoArray {
} }
Ok(children) Ok(children)
} else { } else {
let child_indices = children_index Ok(self
.get(node.proto_node_index) .nodes
.map(|c| c.as_slice())
.unwrap_or(&[]);
Ok(child_indices
.iter() .iter()
.filter_map(|&child_index| { .enumerate()
let child_node = self.nodes.get(child_index)?; .filter(|(_, child_node)| {
if child_node.get_parent_payload_status() != node.payload_status { child_node.parent() == Some(node.proto_node_index)
return None; && child_node.get_parent_payload_status() == node.payload_status
} })
Some(( .map(|(child_index, child_node)| {
(
IndexedForkChoiceNode { IndexedForkChoiceNode {
root: child_node.root(), root: child_node.root(),
proto_node_index: child_index, proto_node_index: child_index,
payload_status: PayloadStatus::Pending, payload_status: PayloadStatus::Pending,
}, },
child_node.clone(), child_node.clone(),
)) )
}) })
.collect()) .collect())
} }