From 263672c7509894137a4c4960b7177dd3fe42275a Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Mon, 2 Oct 2023 11:21:50 +0200 Subject: [PATCH 01/21] decrease size of DirectLink to 4 bytes --- crates/core/src/redpiler/backend/direct.rs | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 7f33752e..aedc891f 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -29,7 +29,7 @@ mod nodes { use std::ops::{Index, IndexMut}; #[derive(Debug, Copy, Clone)] - pub struct NodeId(u32); + pub struct NodeId(pub(crate) u32); impl NodeId { pub fn index(self) -> usize { @@ -93,8 +93,20 @@ mod nodes { #[derive(Debug, Clone, Copy)] struct DirectLink { - weight: u8, - to: NodeId, + data: u32, +} + +impl DirectLink { + pub fn new(to: NodeId, weight: u8) -> Self { + Self { data: (to.0 << 8) | weight as u32 } + } + + pub fn to(&self) -> NodeId { + NodeId(self.data >> 8) + } + pub fn weight(&self) -> u8 { + self.data as u8 + } } #[derive(Debug, Clone, Copy)] @@ -132,9 +144,9 @@ impl NodeType { #[repr(align(128))] pub struct Node { ty: NodeType, - default_inputs: SmallVec<[DirectLink; 7]>, - side_inputs: SmallVec<[DirectLink; 2]>, - updates: SmallVec<[NodeId; 4]>, + default_inputs: SmallVec<[DirectLink; 8]>, + side_inputs: SmallVec<[DirectLink; 4]>, + updates: SmallVec<[NodeId; 10]>, facing_diode: bool, comparator_far_input: Option, @@ -166,10 +178,10 @@ impl Node { // Safety: bounds checked NodeId::from_index(idx) }; - let link = DirectLink { - to: idx, - weight: edge.weight().ss, - }; + let link = DirectLink::new( + idx, + edge.weight().ss, + ); match edge.weight().ty { LinkType::Default => default_inputs.push(link), LinkType::Side => side_inputs.push(link), @@ -179,7 +191,7 @@ impl Node { stats.side_link_count += side_inputs.len(); use crate::redpiler::compile_graph::NodeType as CNodeType; - let updates: SmallVec<[NodeId; 4]> = if node.ty != CNodeType::Constant { + let updates = if node.ty != CNodeType::Constant { graph .neighbors_directed(node_idx, Direction::Outgoing) .map(|idx| unsafe { @@ -566,7 +578,7 @@ fn schedule_tick( } fn link_strength(link: DirectLink, nodes: &Nodes) -> u8 { - nodes[link.to].output_power.saturating_sub(link.weight) + nodes[link.to()].output_power.saturating_sub(link.weight()) } fn get_bool_input(node: &Node, nodes: &Nodes) -> bool { @@ -738,9 +750,9 @@ impl fmt::Display for DirectBackend { write!( f, "n{}->n{}[label=\"{}\"{}];", - link.to.index(), + link.to().index(), id, - link.weight, + link.weight(), color )?; } From da448b43e738033718b6da44756e885209c38672 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Sun, 8 Oct 2023 22:16:03 +0200 Subject: [PATCH 02/21] duplicate signal strength to all outputs This allows input states to be checked with simd because the input nodes don't need to be fetched --- crates/core/src/redpiler/backend/direct.rs | 170 ++++++++++++--------- 1 file changed, 101 insertions(+), 69 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index aedc891f..89673e9c 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -91,6 +91,27 @@ mod nodes { } } +#[derive(Debug, Clone, Copy)] +struct ForwardLink { + data: u32 +} +impl ForwardLink { + pub fn new(id: NodeId, side: bool, ss: u8) -> Self { + assert!(id.0 < (1 << 27)); + assert!(ss < (1 << 4)); + Self { data: id.0 << 5 | if side {1 << 4} else {0} | ss as u32} + } + pub fn node(self) -> NodeId { + NodeId(self.data >> 5) + } + pub fn side(self) -> bool { + self.data & (1 << 4) != 0 + } + pub fn ss(self) -> u8 { + (self.data & 0b1111) as u8 + } +} + #[derive(Debug, Clone, Copy)] struct DirectLink { data: u32, @@ -144,9 +165,10 @@ impl NodeType { #[repr(align(128))] pub struct Node { ty: NodeType, - default_inputs: SmallVec<[DirectLink; 8]>, - side_inputs: SmallVec<[DirectLink; 4]>, - updates: SmallVec<[NodeId; 10]>, + default_inputs: [u8; 16], + side_inputs: [u8; 16], + updates: SmallVec<[ForwardLink; 18]>, + facing_diode: bool, comparator_far_input: Option, @@ -169,22 +191,16 @@ impl Node { ) -> Self { let node = &graph[node_idx]; - let mut default_inputs = SmallVec::new(); - let mut side_inputs = SmallVec::new(); + let mut default_inputs = [0; 16]; + let mut side_inputs = [0; 16]; for edge in graph.edges_directed(node_idx, Direction::Incoming) { - let idx = nodes_map[&edge.source()]; - assert!(idx < nodes_len); - let idx = unsafe { - // Safety: bounds checked - NodeId::from_index(idx) - }; - let link = DirectLink::new( - idx, - edge.weight().ss, - ); - match edge.weight().ty { - LinkType::Default => default_inputs.push(link), - LinkType::Side => side_inputs.push(link), + let weight = edge.weight(); + let distance = weight.ss; + let source = edge.source(); + let ss = graph[source].state.output_strength.saturating_sub(distance); + match weight.ty { + LinkType::Default => default_inputs[ss as usize] += 1, + LinkType::Side => side_inputs[ss as usize] += 1, } } stats.default_link_count += default_inputs.len(); @@ -193,12 +209,16 @@ impl Node { use crate::redpiler::compile_graph::NodeType as CNodeType; let updates = if node.ty != CNodeType::Constant { graph - .neighbors_directed(node_idx, Direction::Outgoing) - .map(|idx| unsafe { + .edges_directed(node_idx, Direction::Outgoing) + .map(|edge| unsafe { + let idx = edge.target(); let idx = nodes_map[&idx]; assert!(idx < nodes_len); // Safety: bounds checked - NodeId::from_index(idx) + let target_id = NodeId::from_index(idx); + + let weight = edge.weight(); + ForwardLink::new(target_id, weight.ty == LinkType::Side, weight.ss) }) .collect() } else { @@ -331,11 +351,27 @@ impl DirectBackend { fn set_node(&mut self, node_id: NodeId, powered: bool, new_power: u8) { let node = &mut self.nodes[node_id]; + let old_power = node.output_power; + node.changed = true; node.powered = powered; node.output_power = new_power; for i in 0..node.updates.len() { - let update = self.nodes[node_id].updates[i]; + let node = &self.nodes[node_id]; + let update_link = node.updates[i]; + let side = update_link.side(); + let distance = update_link.ss(); + let update = update_link.node(); + + let update_ref = &mut self.nodes[update]; + let inputs = if side { + &mut update_ref.side_inputs + } else { + &mut update_ref.default_inputs + }; + inputs[old_power.saturating_sub(distance) as usize] -= 1; + inputs[new_power.saturating_sub(distance) as usize] += 1; + update_node(&mut self.scheduler, &mut self.nodes, update); } } @@ -417,7 +453,7 @@ impl JITBackend for DirectBackend { continue; } - let should_be_powered = get_bool_input(node, &self.nodes); + let should_be_powered = get_bool_input(node); if node.powered && !should_be_powered { self.set_node(node_id, false, 0); } else if !node.powered { @@ -435,7 +471,7 @@ impl JITBackend for DirectBackend { } } NodeType::SimpleRepeater(delay) => { - let should_be_powered = get_bool_input(node, &self.nodes); + let should_be_powered = get_bool_input(node); if node.powered && !should_be_powered { self.set_node(node_id, false, 0); } else if !node.powered { @@ -453,7 +489,7 @@ impl JITBackend for DirectBackend { } } NodeType::Torch => { - let should_be_off = get_bool_input(node, &self.nodes); + let should_be_off = get_bool_input(node); let lit = node.powered; if lit && should_be_off { self.set_node(node_id, false, 0); @@ -462,7 +498,7 @@ impl JITBackend for DirectBackend { } } NodeType::Comparator(mode) => { - let (mut input_power, side_input_power) = get_all_input(node, &self.nodes); + let (mut input_power, side_input_power) = get_all_input(node); if let Some(far_override) = node.comparator_far_input { if input_power < 15 { input_power = far_override; @@ -476,7 +512,7 @@ impl JITBackend for DirectBackend { } } NodeType::Lamp => { - let should_be_lit = get_bool_input(node, &self.nodes); + let should_be_lit = get_bool_input(node); if node.powered && !should_be_lit { self.set_node(node_id, false, 0); } @@ -581,29 +617,21 @@ fn link_strength(link: DirectLink, nodes: &Nodes) -> u8 { nodes[link.to()].output_power.saturating_sub(link.weight()) } -fn get_bool_input(node: &Node, nodes: &Nodes) -> bool { - node.default_inputs - .iter() - .copied() - .any(|link| link_strength(link, nodes) > 0) +const INPUT_MASK: u128 = u128::from_ne_bytes([0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]); + +fn get_bool_input(node: &Node) -> bool { + u128::from_ne_bytes(node.default_inputs) & INPUT_MASK != 0 +} + +fn last_index_positive(array: &[u8; 16]) -> u32 { + let value = u128::from_ne_bytes(*array); + if value == 0 {0} else {15 - (value.leading_zeros() >> 3)} } -fn get_all_input(node: &Node, nodes: &Nodes) -> (u8, u8) { - let input_power = node - .default_inputs - .iter() - .copied() - .map(|link| link_strength(link, nodes)) - .max() - .unwrap_or(0); - - let side_input_power = node - .side_inputs - .iter() - .copied() - .map(|link| link_strength(link, nodes)) - .max() - .unwrap_or(0); +fn get_all_input(node: &Node) -> (u8, u8) { + let input_power = last_index_positive(&node.default_inputs) as u8; + + let side_input_power = last_index_positive(&node.side_inputs) as u8; (input_power, side_input_power) } @@ -614,7 +642,7 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId match node.ty { NodeType::Repeater(delay) => { - let (input_power, side_input_power) = get_all_input(node, nodes); + let (input_power, side_input_power) = get_all_input(node); let node = &mut nodes[node_id]; let should_be_locked = side_input_power > 0; if !node.locked && should_be_locked { @@ -641,7 +669,7 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId if node.pending_tick { return; } - let should_be_powered = get_bool_input(node, nodes); + let should_be_powered = get_bool_input(node); if node.powered != should_be_powered { let priority = if node.facing_diode { TickPriority::Highest @@ -658,7 +686,7 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId if node.pending_tick { return; } - let should_be_off = get_bool_input(node, nodes); + let should_be_off = get_bool_input(node); let lit = node.powered; if lit == should_be_off { let node = &mut nodes[node_id]; @@ -669,7 +697,7 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId if node.pending_tick { return; } - let (mut input_power, side_input_power) = get_all_input(node, nodes); + let (mut input_power, side_input_power) = get_all_input(node); if let Some(far_override) = node.comparator_far_input { if input_power < 15 { input_power = far_override; @@ -688,7 +716,7 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId } } NodeType::Lamp => { - let should_be_lit = get_bool_input(node, nodes); + let should_be_lit = get_bool_input(node); let lit = node.powered; let node = &mut nodes[node_id]; if lit && !should_be_lit { @@ -698,14 +726,14 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId } } NodeType::Trapdoor => { - let should_be_powered = get_bool_input(node, nodes); + let should_be_powered = get_bool_input(node); if node.powered != should_be_powered { let node = &mut nodes[node_id]; set_node(node, should_be_powered); } } NodeType::Wire => { - let (input_power, _) = get_all_input(node, nodes); + let (input_power, _) = get_all_input(node); if node.output_power != input_power { let node = &mut nodes[node_id]; node.output_power = input_power; @@ -742,20 +770,24 @@ impl fmt::Display for DirectBackend { .iter() .map(|link| (LinkType::Default, link)) .chain(node.side_inputs.iter().map(|link| (LinkType::Side, link))); - for (link_type, link) in all_inputs { - let color = match link_type { - LinkType::Default => "", - LinkType::Side => ",color=\"blue\"", - }; - write!( - f, - "n{}->n{}[label=\"{}\"{}];", - link.to().index(), - id, - link.weight(), - color - )?; - } + // TODO: fix direct graph serialization + // for (link_type, link) in all_inputs { + // let color = match link_type { + // LinkType::Default => "", + // LinkType::Side => ",color=\"blue\"", + // }; + // write!( + // f, + // "n{}->n{}[label=\"{}\"{}];", + // link.to().index(), + // id, + // link.weight(), + // color + // )?; + // } + + + // for update in &node.updates { // write!(f, "n{}->n{}[style=dotted];", id, update)?; // } From 41f830c03d03f808bdee0674c7870c248f92fcaa Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Mon, 9 Oct 2023 18:39:38 +0200 Subject: [PATCH 03/21] cleanup old code --- crates/core/src/redpiler/backend/direct.rs | 62 +++++----------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 89673e9c..fe2fd908 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -112,24 +112,6 @@ impl ForwardLink { } } -#[derive(Debug, Clone, Copy)] -struct DirectLink { - data: u32, -} - -impl DirectLink { - pub fn new(to: NodeId, weight: u8) -> Self { - Self { data: (to.0 << 8) | weight as u32 } - } - - pub fn to(&self) -> NodeId { - NodeId(self.data >> 8) - } - pub fn weight(&self) -> u8 { - self.data as u8 - } -} - #[derive(Debug, Clone, Copy)] enum NodeType { Repeater(u8), @@ -224,6 +206,7 @@ impl Node { } else { SmallVec::new() }; + *stats.update_groups.entry(updates.len()).or_insert(0) += 1; stats.update_link_count += updates.len(); let ty = match node.ty { @@ -613,10 +596,6 @@ fn schedule_tick( scheduler.schedule_tick(node_id, delay, priority); } -fn link_strength(link: DirectLink, nodes: &Nodes) -> u8 { - nodes[link.to()].output_power.saturating_sub(link.weight()) -} - const INPUT_MASK: u128 = u128::from_ne_bytes([0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]); fn get_bool_input(node: &Node) -> bool { @@ -765,32 +744,19 @@ impl fmt::Display for DirectBackend { "No Pos".to_string() }; write!(f, "n{}[label=\"{}\\n({})\"];", id, label, pos,)?; - let all_inputs = node - .default_inputs - .iter() - .map(|link| (LinkType::Default, link)) - .chain(node.side_inputs.iter().map(|link| (LinkType::Side, link))); - // TODO: fix direct graph serialization - // for (link_type, link) in all_inputs { - // let color = match link_type { - // LinkType::Default => "", - // LinkType::Side => ",color=\"blue\"", - // }; - // write!( - // f, - // "n{}->n{}[label=\"{}\"{}];", - // link.to().index(), - // id, - // link.weight(), - // color - // )?; - // } - - - - // for update in &node.updates { - // write!(f, "n{}->n{}[style=dotted];", id, update)?; - // } + for link in node.updates.iter() { + let out_index = link.node().index(); + let distance = link.ss(); + let color = if link.side() {",color=\"blue\""} else {""}; + write!( + f, + "n{}->n{}[label=\"{}\"{}];", + id, + out_index, + distance, + color + )?; + } } f.write_str("}\n") } From f1f3806dce0b70a33a29f13c5c992a7ff7f29077 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Mon, 9 Oct 2023 20:25:56 +0200 Subject: [PATCH 04/21] remove erroring line --- crates/core/src/redpiler/backend/direct.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index fe2fd908..db4d31b2 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -206,7 +206,6 @@ impl Node { } else { SmallVec::new() }; - *stats.update_groups.entry(updates.len()).or_insert(0) += 1; stats.update_link_count += updates.len(); let ty = match node.ty { From c4cce0715454a93c08d2fcec7451d05683ca343b Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Sun, 15 Oct 2023 12:08:07 +0200 Subject: [PATCH 05/21] make index of NodeId private again --- crates/core/src/redpiler/backend/direct.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index db4d31b2..774a189b 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -29,7 +29,7 @@ mod nodes { use std::ops::{Index, IndexMut}; #[derive(Debug, Copy, Clone)] - pub struct NodeId(pub(crate) u32); + pub struct NodeId(u32); impl NodeId { pub fn index(self) -> usize { @@ -97,12 +97,15 @@ struct ForwardLink { } impl ForwardLink { pub fn new(id: NodeId, side: bool, ss: u8) -> Self { - assert!(id.0 < (1 << 27)); + assert!(id.index() < (1 << 27)); assert!(ss < (1 << 4)); - Self { data: id.0 << 5 | if side {1 << 4} else {0} | ss as u32} + Self { data: (id.index() as u32) << 5 | if side {1 << 4} else {0} | ss as u32} } pub fn node(self) -> NodeId { - NodeId(self.data >> 5) + unsafe { + // safety: ForwardLink is contructed using a NodeId + NodeId::from_index((self.data >> 5) as usize) + } } pub fn side(self) -> bool { self.data & (1 << 4) != 0 @@ -353,7 +356,7 @@ impl DirectBackend { }; inputs[old_power.saturating_sub(distance) as usize] -= 1; inputs[new_power.saturating_sub(distance) as usize] += 1; - + update_node(&mut self.scheduler, &mut self.nodes, update); } } From 5ec5e71af7dc7918c896eb6590bfc31ccfa90e57 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Sun, 15 Oct 2023 12:21:25 +0200 Subject: [PATCH 06/21] Make TickScheduler::NUM_QUEUES constant --- crates/core/src/redpiler/backend/direct.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 774a189b..3a384a5f 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -259,12 +259,13 @@ impl Queues { #[derive(Default)] struct TickScheduler { - queues_deque: [Queues; 16], + queues_deque: [Queues; Self::NUM_QUEUES], pos: usize, } impl TickScheduler { const NUM_PRIORITIES: usize = 4; + const NUM_QUEUES: usize = 16; fn reset(&mut self, world: &mut W, blocks: &[Option<(BlockPos, Block)>]) { for (idx, queues) in self.queues_deque.iter().enumerate() { @@ -287,11 +288,11 @@ impl TickScheduler { } fn schedule_tick(&mut self, node: NodeId, delay: usize, priority: TickPriority) { - self.queues_deque[(self.pos + delay) & 15].0[Self::priority_index(priority)].push(node); + self.queues_deque[(self.pos + delay) % Self::NUM_QUEUES].0[Self::priority_index(priority)].push(node); } fn queues_this_tick(&mut self) -> Queues { - self.pos = (self.pos + 1) & 15; + self.pos = (self.pos + 1) % Self::NUM_QUEUES; mem::take(&mut self.queues_deque[self.pos]) } From c7edd2f4ff8c6245218bde750f81870182591d93 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Sun, 15 Oct 2023 12:41:05 +0200 Subject: [PATCH 07/21] remove from_ne_bytes from last_index_positive --- crates/core/src/redpiler/backend/direct.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 3a384a5f..855d99fa 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -606,7 +606,8 @@ fn get_bool_input(node: &Node) -> bool { } fn last_index_positive(array: &[u8; 16]) -> u32 { - let value = u128::from_ne_bytes(*array); + // Note: this might be slower on big-endian systems + let value = u128::from_le_bytes(*array); if value == 0 {0} else {15 - (value.leading_zeros() >> 3)} } From 4c3e3ad49db297812a680347a778ddc93c523aab Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Tue, 17 Oct 2023 19:39:53 +0200 Subject: [PATCH 08/21] only get needed repeater inputs --- crates/core/src/redpiler/backend/direct.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 855d99fa..c67ac6e5 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -605,6 +605,10 @@ fn get_bool_input(node: &Node) -> bool { u128::from_ne_bytes(node.default_inputs) & INPUT_MASK != 0 } +fn get_bool_side(node: &Node) -> bool { + u128::from_ne_bytes(node.side_inputs) & INPUT_MASK != 0 +} + fn last_index_positive(array: &[u8; 16]) -> u32 { // Note: this might be slower on big-endian systems let value = u128::from_le_bytes(*array); @@ -625,9 +629,8 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId match node.ty { NodeType::Repeater(delay) => { - let (input_power, side_input_power) = get_all_input(node); let node = &mut nodes[node_id]; - let should_be_locked = side_input_power > 0; + let should_be_locked = get_bool_side(node); if !node.locked && should_be_locked { set_node_locked(node, true); } else if node.locked && !should_be_locked { @@ -635,7 +638,7 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId } if !node.locked && !node.pending_tick { - let should_be_powered = input_power > 0; + let should_be_powered = get_bool_input(node); if should_be_powered != node.powered { let priority = if node.facing_diode { TickPriority::Highest From 9994ba0ac16bfa1f623a1fb73f5fd3f5eab4c3fe Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Fri, 1 Dec 2023 20:13:32 +0100 Subject: [PATCH 09/21] replace magic number with NUM_QUEUES --- crates/core/src/redpiler/backend/direct.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index c67ac6e5..ac8e8d05 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -269,7 +269,7 @@ impl TickScheduler { fn reset(&mut self, world: &mut W, blocks: &[Option<(BlockPos, Block)>]) { for (idx, queues) in self.queues_deque.iter().enumerate() { - let delay = if self.pos >= idx { idx + 16 } else { idx } - self.pos; + let delay = if self.pos >= idx { idx + Self::NUM_QUEUES } else { idx } - self.pos; for (entries, priority) in queues.0.iter().zip(Self::priorities()) { for node in entries { let Some((pos, _)) = blocks[node.index()] else { From 3026677b34deec6f5d14a1e5b14a5917104c41cb Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Wed, 6 Dec 2023 00:26:26 +0100 Subject: [PATCH 10/21] don't crash the program when ss is larger than 15 when creating a forward link --- crates/core/src/redpiler/backend/direct.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index ac8e8d05..1f92e58f 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -96,9 +96,11 @@ struct ForwardLink { data: u32 } impl ForwardLink { - pub fn new(id: NodeId, side: bool, ss: u8) -> Self { + pub fn new(id: NodeId, side: bool, mut ss: u8) -> Self { assert!(id.index() < (1 << 27)); - assert!(ss < (1 << 4)); + if ss >= 16 { + ss = 15; + } Self { data: (id.index() as u32) << 5 | if side {1 << 4} else {0} | ss as u32} } pub fn node(self) -> NodeId { From 20df8620372dd507bcf2b0be2dd7b028dfa81e63 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Wed, 6 Dec 2023 00:26:50 +0100 Subject: [PATCH 11/21] fix minor typo --- crates/core/src/redpiler/backend/direct.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 1f92e58f..091c769f 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -105,7 +105,7 @@ impl ForwardLink { } pub fn node(self) -> NodeId { unsafe { - // safety: ForwardLink is contructed using a NodeId + // safety: ForwardLink is constructed using a NodeId NodeId::from_index((self.data >> 5) as usize) } } From 70e5b43ddfec953d380dd4304ab40100152507b0 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Wed, 6 Dec 2023 00:27:29 +0100 Subject: [PATCH 12/21] rename INPUT_MASK to BOOL_INPUT_MASK --- crates/core/src/redpiler/backend/direct.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 091c769f..b4f80bbd 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -601,14 +601,14 @@ fn schedule_tick( scheduler.schedule_tick(node_id, delay, priority); } -const INPUT_MASK: u128 = u128::from_ne_bytes([0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]); +const BOOL_INPUT_MASK: u128 = u128::from_ne_bytes([0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]); fn get_bool_input(node: &Node) -> bool { - u128::from_ne_bytes(node.default_inputs) & INPUT_MASK != 0 + u128::from_ne_bytes(node.default_inputs) & BOOL_INPUT_MASK != 0 } fn get_bool_side(node: &Node) -> bool { - u128::from_ne_bytes(node.side_inputs) & INPUT_MASK != 0 + u128::from_ne_bytes(node.side_inputs) & BOOL_INPUT_MASK != 0 } fn last_index_positive(array: &[u8; 16]) -> u32 { From 5b3a0350a7690da891a1898e05258849ba973fa0 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Wed, 6 Dec 2023 00:34:49 +0100 Subject: [PATCH 13/21] Run cargo fmt --- crates/core/src/redpiler/backend/direct.rs | 36 ++++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index b4f80bbd..7807cae6 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -93,7 +93,7 @@ mod nodes { #[derive(Debug, Clone, Copy)] struct ForwardLink { - data: u32 + data: u32, } impl ForwardLink { pub fn new(id: NodeId, side: bool, mut ss: u8) -> Self { @@ -101,7 +101,9 @@ impl ForwardLink { if ss >= 16 { ss = 15; } - Self { data: (id.index() as u32) << 5 | if side {1 << 4} else {0} | ss as u32} + Self { + data: (id.index() as u32) << 5 | if side { 1 << 4 } else { 0 } | ss as u32, + } } pub fn node(self) -> NodeId { unsafe { @@ -155,7 +157,7 @@ pub struct Node { default_inputs: [u8; 16], side_inputs: [u8; 16], updates: SmallVec<[ForwardLink; 18]>, - + facing_diode: bool, comparator_far_input: Option, @@ -203,7 +205,7 @@ impl Node { assert!(idx < nodes_len); // Safety: bounds checked let target_id = NodeId::from_index(idx); - + let weight = edge.weight(); ForwardLink::new(target_id, weight.ty == LinkType::Side, weight.ss) }) @@ -271,7 +273,11 @@ impl TickScheduler { fn reset(&mut self, world: &mut W, blocks: &[Option<(BlockPos, Block)>]) { for (idx, queues) in self.queues_deque.iter().enumerate() { - let delay = if self.pos >= idx { idx + Self::NUM_QUEUES } else { idx } - self.pos; + let delay = if self.pos >= idx { + idx + Self::NUM_QUEUES + } else { + idx + } - self.pos; for (entries, priority) in queues.0.iter().zip(Self::priorities()) { for node in entries { let Some((pos, _)) = blocks[node.index()] else { @@ -290,7 +296,8 @@ impl TickScheduler { } fn schedule_tick(&mut self, node: NodeId, delay: usize, priority: TickPriority) { - self.queues_deque[(self.pos + delay) % Self::NUM_QUEUES].0[Self::priority_index(priority)].push(node); + self.queues_deque[(self.pos + delay) % Self::NUM_QUEUES].0[Self::priority_index(priority)] + .push(node); } fn queues_this_tick(&mut self) -> Queues { @@ -601,7 +608,9 @@ fn schedule_tick( scheduler.schedule_tick(node_id, delay, priority); } -const BOOL_INPUT_MASK: u128 = u128::from_ne_bytes([0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]); +const BOOL_INPUT_MASK: u128 = u128::from_ne_bytes([ + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +]); fn get_bool_input(node: &Node) -> bool { u128::from_ne_bytes(node.default_inputs) & BOOL_INPUT_MASK != 0 @@ -614,7 +623,11 @@ fn get_bool_side(node: &Node) -> bool { fn last_index_positive(array: &[u8; 16]) -> u32 { // Note: this might be slower on big-endian systems let value = u128::from_le_bytes(*array); - if value == 0 {0} else {15 - (value.leading_zeros() >> 3)} + if value == 0 { + 0 + } else { + 15 - (value.leading_zeros() >> 3) + } } fn get_all_input(node: &Node) -> (u8, u8) { @@ -756,14 +769,11 @@ impl fmt::Display for DirectBackend { for link in node.updates.iter() { let out_index = link.node().index(); let distance = link.ss(); - let color = if link.side() {",color=\"blue\""} else {""}; + let color = if link.side() { ",color=\"blue\"" } else { "" }; write!( f, "n{}->n{}[label=\"{}\"{}];", - id, - out_index, - distance, - color + id, out_index, distance, color )?; } } From 0d8b93102d6eccf3bfeb85903b3b339d21625074 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Wed, 6 Dec 2023 00:42:13 +0100 Subject: [PATCH 14/21] address nits --- crates/core/src/redpiler/backend/direct.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 7807cae6..1d962716 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -95,6 +95,7 @@ mod nodes { struct ForwardLink { data: u32, } + impl ForwardLink { pub fn new(id: NodeId, side: bool, mut ss: u8) -> Self { assert!(id.index() < (1 << 27)); @@ -105,15 +106,18 @@ impl ForwardLink { data: (id.index() as u32) << 5 | if side { 1 << 4 } else { 0 } | ss as u32, } } + pub fn node(self) -> NodeId { unsafe { // safety: ForwardLink is constructed using a NodeId NodeId::from_index((self.data >> 5) as usize) } } + pub fn side(self) -> bool { self.data & (1 << 4) != 0 } + pub fn ss(self) -> u8 { (self.data & 0b1111) as u8 } From ffe264ca6a32b264b93b2457b9d08c497d18a39e Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Fri, 8 Dec 2023 20:31:02 +0100 Subject: [PATCH 15/21] Add check for maximum inputs. This isn't really neccesary right now but if we ever add optimizations which combine nodes it might become likely that a node will have more than 255 inputs. --- crates/core/src/redpiler/backend/direct.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 1d962716..21f01f55 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -183,6 +183,11 @@ impl Node { stats: &mut FinalGraphStats, ) -> Self { let node = &graph[node_idx]; + + const MAX_INPUTS: usize = 255; + + let mut default_input_count = 0; + let mut side_input_count = 0; let mut default_inputs = [0; 16]; let mut side_inputs = [0; 16]; @@ -192,8 +197,20 @@ impl Node { let source = edge.source(); let ss = graph[source].state.output_strength.saturating_sub(distance); match weight.ty { - LinkType::Default => default_inputs[ss as usize] += 1, - LinkType::Side => side_inputs[ss as usize] += 1, + LinkType::Default => { + if default_input_count >= MAX_INPUTS { + panic!("Exceeded the maximum number of default inputs {}", MAX_INPUTS); + } + default_input_count += 1; + default_inputs[ss as usize] += 1; + }, + LinkType::Side => { + if side_input_count >= MAX_INPUTS { + panic!("Exceeded the maximum number of side inputs {}", MAX_INPUTS); + } + side_input_count += 1; + side_inputs[ss as usize] += 1; + } } } stats.default_link_count += default_inputs.len(); From 2120aa5633551e1f1f87c9a19a8136ad8f9d5b13 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Sun, 10 Dec 2023 17:57:40 +0100 Subject: [PATCH 16/21] make poper use of input counters in from_compile_node --- crates/core/src/redpiler/backend/direct.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 21f01f55..00199a4a 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -213,8 +213,8 @@ impl Node { } } } - stats.default_link_count += default_inputs.len(); - stats.side_link_count += side_inputs.len(); + stats.default_link_count += default_input_count; + stats.side_link_count += side_input_count; use crate::redpiler::compile_graph::NodeType as CNodeType; let updates = if node.ty != CNodeType::Constant { @@ -238,7 +238,7 @@ impl Node { let ty = match node.ty { CNodeType::Repeater(delay) => { - if side_inputs.is_empty() { + if side_input_count == 0 { NodeType::SimpleRepeater(delay) } else { NodeType::Repeater(delay) From ba7042d0e8046b785d134fa4433a3680f97d497b Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Mon, 11 Dec 2023 19:47:57 +0100 Subject: [PATCH 17/21] Make clamp_weights pass mandatory --- crates/core/src/redpiler/backend/direct.rs | 18 ++++++++++-------- .../core/src/redpiler/passes/clamp_weights.rs | 5 +++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 00199a4a..18da7c63 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -97,11 +97,10 @@ struct ForwardLink { } impl ForwardLink { - pub fn new(id: NodeId, side: bool, mut ss: u8) -> Self { + pub fn new(id: NodeId, side: bool, ss: u8) -> Self { assert!(id.index() < (1 << 27)); - if ss >= 16 { - ss = 15; - } + // the clamp_weights compile pass should ensure ss < 16 + assert!(ss < 16); Self { data: (id.index() as u32) << 5 | if side { 1 << 4 } else { 0 } | ss as u32, } @@ -183,9 +182,9 @@ impl Node { stats: &mut FinalGraphStats, ) -> Self { let node = &graph[node_idx]; - + const MAX_INPUTS: usize = 255; - + let mut default_input_count = 0; let mut side_input_count = 0; @@ -199,11 +198,14 @@ impl Node { match weight.ty { LinkType::Default => { if default_input_count >= MAX_INPUTS { - panic!("Exceeded the maximum number of default inputs {}", MAX_INPUTS); + panic!( + "Exceeded the maximum number of default inputs {}", + MAX_INPUTS + ); } default_input_count += 1; default_inputs[ss as usize] += 1; - }, + } LinkType::Side => { if side_input_count >= MAX_INPUTS { panic!("Exceeded the maximum number of side inputs {}", MAX_INPUTS); diff --git a/crates/core/src/redpiler/passes/clamp_weights.rs b/crates/core/src/redpiler/passes/clamp_weights.rs index d0a0e88f..33f50e7c 100644 --- a/crates/core/src/redpiler/passes/clamp_weights.rs +++ b/crates/core/src/redpiler/passes/clamp_weights.rs @@ -9,4 +9,9 @@ impl Pass for ClampWeights { fn run_pass(&self, graph: &mut CompileGraph, _: &CompilerOptions, _: &CompilerInput<'_, W>) { graph.retain_edges(|g, edge| g[edge].ss < 15); } + + fn should_run(&self, _: &CompilerOptions) -> bool { + // Mandatory + true + } } From 4bca713ea9241d7c28cde214947849c6e3756508 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Tue, 12 Dec 2023 22:01:53 +0100 Subject: [PATCH 18/21] Use get_unchecked_mut() for incrementing signal strength counters --- crates/core/src/redpiler/backend/direct.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 18da7c63..8674e854 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -387,8 +387,15 @@ impl DirectBackend { } else { &mut update_ref.default_inputs }; - inputs[old_power.saturating_sub(distance) as usize] -= 1; - inputs[new_power.saturating_sub(distance) as usize] += 1; + + let old_power = old_power.saturating_sub(distance); + let new_power = new_power.saturating_sub(distance); + + // Safety: signal strength is never larger than 15 + unsafe { + *inputs.get_unchecked_mut(old_power as usize) -= 1; + *inputs.get_unchecked_mut(new_power as usize) += 1; + } update_node(&mut self.scheduler, &mut self.nodes, update); } From ee302e9d9dc4bb5d31a966372991fa606f7f10ed Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Tue, 12 Dec 2023 22:02:25 +0100 Subject: [PATCH 19/21] skip updating if output power did not change --- crates/core/src/redpiler/backend/direct.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 8674e854..a9ec9929 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -391,6 +391,10 @@ impl DirectBackend { let old_power = old_power.saturating_sub(distance); let new_power = new_power.saturating_sub(distance); + if old_power == new_power { + continue; + } + // Safety: signal strength is never larger than 15 unsafe { *inputs.get_unchecked_mut(old_power as usize) -= 1; From a7e79cc537436d4998dd88c9e1625dc3d5363cc4 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Tue, 12 Dec 2023 22:58:11 +0100 Subject: [PATCH 20/21] wrap input arrays into a struct to ensure they are aligned --- crates/core/src/redpiler/backend/direct.rs | 30 +++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index a9ec9929..9fa18a4c 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -151,14 +151,20 @@ impl NodeType { } } +#[repr(align(16))] +#[derive(Debug, Clone, Default)] +struct NodeInput { + ss_counts: [u8; 16], +} + // struct is 128 bytes to fit nicely into cachelines // which are usualy 64 bytes, it can vary but is almost always a power of 2 #[derive(Debug, Clone)] #[repr(align(128))] pub struct Node { ty: NodeType, - default_inputs: [u8; 16], - side_inputs: [u8; 16], + default_inputs: NodeInput, + side_inputs: NodeInput, updates: SmallVec<[ForwardLink; 18]>, facing_diode: bool, @@ -188,8 +194,8 @@ impl Node { let mut default_input_count = 0; let mut side_input_count = 0; - let mut default_inputs = [0; 16]; - let mut side_inputs = [0; 16]; + let mut default_inputs = NodeInput {ss_counts: [0; 16]}; + let mut side_inputs = NodeInput {ss_counts: [0; 16]}; for edge in graph.edges_directed(node_idx, Direction::Incoming) { let weight = edge.weight(); let distance = weight.ss; @@ -204,14 +210,14 @@ impl Node { ); } default_input_count += 1; - default_inputs[ss as usize] += 1; + default_inputs.ss_counts[ss as usize] += 1; } LinkType::Side => { if side_input_count >= MAX_INPUTS { panic!("Exceeded the maximum number of side inputs {}", MAX_INPUTS); } side_input_count += 1; - side_inputs[ss as usize] += 1; + side_inputs.ss_counts[ss as usize] += 1; } } } @@ -397,8 +403,8 @@ impl DirectBackend { // Safety: signal strength is never larger than 15 unsafe { - *inputs.get_unchecked_mut(old_power as usize) -= 1; - *inputs.get_unchecked_mut(new_power as usize) += 1; + *inputs.ss_counts.get_unchecked_mut(old_power as usize) -= 1; + *inputs.ss_counts.get_unchecked_mut(new_power as usize) += 1; } update_node(&mut self.scheduler, &mut self.nodes, update); @@ -647,11 +653,11 @@ const BOOL_INPUT_MASK: u128 = u128::from_ne_bytes([ ]); fn get_bool_input(node: &Node) -> bool { - u128::from_ne_bytes(node.default_inputs) & BOOL_INPUT_MASK != 0 + u128::from_le_bytes(node.default_inputs.ss_counts) & BOOL_INPUT_MASK != 0 } fn get_bool_side(node: &Node) -> bool { - u128::from_ne_bytes(node.side_inputs) & BOOL_INPUT_MASK != 0 + u128::from_le_bytes(node.side_inputs.ss_counts) & BOOL_INPUT_MASK != 0 } fn last_index_positive(array: &[u8; 16]) -> u32 { @@ -665,9 +671,9 @@ fn last_index_positive(array: &[u8; 16]) -> u32 { } fn get_all_input(node: &Node) -> (u8, u8) { - let input_power = last_index_positive(&node.default_inputs) as u8; + let input_power = last_index_positive(&node.default_inputs.ss_counts) as u8; - let side_input_power = last_index_positive(&node.side_inputs) as u8; + let side_input_power = last_index_positive(&node.side_inputs.ss_counts) as u8; (input_power, side_input_power) } From 9c9e22f1023924fc77d4d6453eb87362fc018629 Mon Sep 17 00:00:00 2001 From: Bram Otte Date: Thu, 14 Dec 2023 17:55:02 +0100 Subject: [PATCH 21/21] run cargo fmt --- crates/core/src/redpiler/backend/direct.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index 9fa18a4c..fcc75203 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -194,8 +194,8 @@ impl Node { let mut default_input_count = 0; let mut side_input_count = 0; - let mut default_inputs = NodeInput {ss_counts: [0; 16]}; - let mut side_inputs = NodeInput {ss_counts: [0; 16]}; + let mut default_inputs = NodeInput { ss_counts: [0; 16] }; + let mut side_inputs = NodeInput { ss_counts: [0; 16] }; for edge in graph.edges_directed(node_idx, Direction::Incoming) { let weight = edge.weight(); let distance = weight.ss; @@ -393,7 +393,7 @@ impl DirectBackend { } else { &mut update_ref.default_inputs }; - + let old_power = old_power.saturating_sub(distance); let new_power = new_power.saturating_sub(distance); @@ -403,7 +403,7 @@ impl DirectBackend { // Safety: signal strength is never larger than 15 unsafe { - *inputs.ss_counts.get_unchecked_mut(old_power as usize) -= 1; + *inputs.ss_counts.get_unchecked_mut(old_power as usize) -= 1; *inputs.ss_counts.get_unchecked_mut(new_power as usize) += 1; }