From 14d9019436512a3d43453fff60020aac613b3c4f Mon Sep 17 00:00:00 2001 From: Eric Buehler Date: Fri, 25 Apr 2025 05:49:55 -0400 Subject: [PATCH 1/3] Add constant folding --- constensor-core/src/graph.rs | 55 +++++++++++++++++++++++++++++++++-- graph.png | Bin 40087 -> 19107 bytes 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/constensor-core/src/graph.rs b/constensor-core/src/graph.rs index 152fc75..7b4c751 100644 --- a/constensor-core/src/graph.rs +++ b/constensor-core/src/graph.rs @@ -207,6 +207,50 @@ impl Graph { Ok(()) } + /// Optimize by performing constant folding: + /// - Fold BinaryOp and UnaryOp when all operands are constant Fill ops. + fn optimize_const(&mut self) { + // Clone current ops for inspection + let ops = self.data.read().unwrap().clone(); + let mut new_ops = ops.clone(); + for (i, node) in ops.iter().enumerate() { + match &node.op { + Op::BinaryOp { + l_id, + r_id, + operator, + } => { + let l_idx = l_id.get(); + let r_idx = r_id.get(); + // both operands are constant fills + if let Op::Fill { v: v1 } = &new_ops[l_idx].op { + if let Op::Fill { v: v2 } = &new_ops[r_idx].op { + let v = operator.as_closure()(*v1, *v2); + new_ops[i] = GraphNode { + op: Op::Fill { v }, + shape: node.shape.clone(), + }; + } + } + } + Op::UnaryOp { v_id, operator } => { + let idx = v_id.get(); + // operand is a constant fill + if let Op::Fill { v: v0 } = &new_ops[idx].op { + let v = operator.to_closure()(*v0); + new_ops[i] = GraphNode { + op: Op::Fill { v }, + shape: node.shape.clone(), + }; + } + } + _ => {} + } + } + // Commit folded constants + *self.data.write().unwrap() = new_ops; + } + /// Optimize by looking for mul-add pairs, convert to FMA fn optimize_fma(&mut self) { let ops = self.data.write().unwrap().clone(); @@ -434,9 +478,16 @@ impl Graph { /// Optimize this graph. /// - /// Apply the following optimizations - /// - Fuse mul,add + /// Apply the following optimizations: + /// - Constant folding of elementwise fills + /// - Fuse mul-add into FMA + /// - Inplace binary operations when safe + /// - Inplace fused multiply-add when safe + /// - Inplace matrix-multiplication when safe pub fn optimize(&mut self) { + // Constant folding first + self.optimize_const(); + // Fuse mul-add into FMA self.optimize_fma(); self.optimize_inplace_bin(); self.optimize_inplace_fma(); diff --git a/graph.png b/graph.png index 6f0d711fd5b5140b44c6d4c4cac793fb22cb6602..c259f05d10be840760207a61812fc801d416042b 100644 GIT binary patch literal 19107 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 literal 40087 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 iN_#|uux*V1 From 287b3cdc126b5682686b955ba743d37a791fea34 Mon Sep 17 00:00:00 2001 From: Eric Buehler Date: Fri, 25 Apr 2025 05:53:34 -0400 Subject: [PATCH 2/3] Add dead code removal --- constensor-core/src/graph.rs | 96 +++++++++++++++++++++++++++++++++++ graph.png | Bin 19107 -> 4348 bytes 2 files changed, 96 insertions(+) diff --git a/constensor-core/src/graph.rs b/constensor-core/src/graph.rs index 7b4c751..6a3ba25 100644 --- a/constensor-core/src/graph.rs +++ b/constensor-core/src/graph.rs @@ -476,6 +476,99 @@ impl Graph { *self.data.write().unwrap() = new_ops; } + /// Remove nodes whose outputs are never used, except the final output node. + fn optimize_dead_code(&mut self) { + // Clone current ops + let old_ops = self.data.read().unwrap().clone(); + let n = old_ops.len(); + // Mark reachable nodes: start from final output + let mut keep = vec![false; n]; + if n > 0 { + keep[n - 1] = true; + } + // Propagate reachability backwards + for i in (0..n).rev() { + if keep[i] { + match &old_ops[i].op { + Op::BinaryOp { l_id, r_id, .. } => { + keep[l_id.get()] = true; + keep[r_id.get()] = true; + } + Op::UnaryOp { v_id, .. } => { + keep[v_id.get()] = true; + } + Op::FusedMulAdd { + a_id, b_id, c_id, .. + } => { + keep[a_id.get()] = true; + keep[b_id.get()] = true; + keep[c_id.get()] = true; + } + Op::MatMul { + l_id, r_id, o_id, .. + } => { + keep[l_id.get()] = true; + keep[r_id.get()] = true; + if let Some(o_id) = o_id { + keep[o_id.get()] = true; + } + } + _ => {} + } + } + } + // Build new ops and map old indices to new indices + let mut index_map = std::collections::HashMap::new(); + let mut new_ops = Vec::new(); + for (old_idx, node) in old_ops.into_iter().enumerate() { + if keep[old_idx] { + let new_idx = new_ops.len(); + index_map.insert(old_idx, new_idx); + new_ops.push(node); + } + } + // Update tensor IDs in remaining ops + for node in new_ops.iter_mut() { + match &mut node.op { + Op::BinaryOp { l_id, r_id, .. } => { + let old_l = l_id.get(); + let old_r = r_id.get(); + l_id.set(*index_map.get(&old_l).unwrap()); + r_id.set(*index_map.get(&old_r).unwrap()); + } + Op::UnaryOp { v_id, .. } => { + let old_v = v_id.get(); + v_id.set(*index_map.get(&old_v).unwrap()); + } + Op::FusedMulAdd { + a_id, b_id, c_id, .. + } => { + let old_a = a_id.get(); + let old_b = b_id.get(); + let old_c = c_id.get(); + a_id.set(*index_map.get(&old_a).unwrap()); + b_id.set(*index_map.get(&old_b).unwrap()); + c_id.set(*index_map.get(&old_c).unwrap()); + } + Op::MatMul { + l_id, r_id, o_id, .. + } => { + let old_l = l_id.get(); + let old_r = r_id.get(); + l_id.set(*index_map.get(&old_l).unwrap()); + r_id.set(*index_map.get(&old_r).unwrap()); + if let Some(o_id) = o_id { + let old_o = o_id.get(); + o_id.set(*index_map.get(&old_o).unwrap()); + } + } + _ => {} + } + } + // Commit pruned graph + *self.data.write().unwrap() = new_ops; + } + /// Optimize this graph. /// /// Apply the following optimizations: @@ -484,6 +577,7 @@ impl Graph { /// - Inplace binary operations when safe /// - Inplace fused multiply-add when safe /// - Inplace matrix-multiplication when safe + /// - Dead code removal pub fn optimize(&mut self) { // Constant folding first self.optimize_const(); @@ -492,6 +586,8 @@ impl Graph { self.optimize_inplace_bin(); self.optimize_inplace_fma(); self.optimize_inplace_matmul(); + // Remove dead code + self.optimize_dead_code(); } pub fn compile(self) -> Result> { diff --git a/graph.png b/graph.png index c259f05d10be840760207a61812fc801d416042b..584fddd098184ce1d05f049f84849596ba5668de 100644 GIT binary patch literal 4348 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 From 02610add72f6296187edfdb5e7a61eae4a76fa67 Mon Sep 17 00:00:00 2001 From: Eric Buehler Date: Fri, 25 Apr 2025 05:58:48 -0400 Subject: [PATCH 3/3] Readme --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 45032cc..465ab81 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,15 @@ ML framework featuring compile time checks and accelerated by a JIT compiler.

    -Constensor is a fast alternative to Candle which provides the following key features: +Constensor is a fast ML framework which provides the following key features: + - **Compile time shape, dtype, and device checking**: Develop quickly and handle common errors - **Opt-in half precision support**: Run on any GPU -- **Elementwise JIT kernel fusion**: Accelerate CUDA kernels automatically by fusing binary and unary operations - - Fuse binary operations into one kernel - - Use device specific operations such as [`fma`](https://docs.nvidia.com/cuda/cuda-math-api/group__CUDA__MATH__DOUBLE.html#group__CUDA__MATH__DOUBLE_1gff2117f6f3c4ff8a2aa4ce48a0ff2070) to accelerate. -- **Automatic inplacing**: Avoid duplicate allocations +- **Advanced AI compiler features:** + - Elementwise JIT kernel fusion + - Automatic inplacing + - Constant folding + - Dead code removal ```rust