Common subexpression elimination (CSE) is a compiler optimization that identifies and eliminates redundant calculations of identical expressions within a computational graph, caching and reusing the computed result to improve runtime efficiency and reduce computational overhead. In the context of neural network inference, this involves analyzing the model's intermediate representation (IR) to find operations—like repeated matrix multiplications or activation functions on the same inputs—that produce identical outputs, then restructuring the graph to compute them once. This directly reduces FLOPs (floating-point operations) and memory traffic, which is critical for on-device model compression and energy-efficient inference on constrained hardware.
Glossary
Common Subexpression Elimination

What is Common Subexpression Elimination?
Common subexpression elimination (CSE) is a fundamental compiler optimization technique used to improve the efficiency of computational graphs, particularly in machine learning inference frameworks and on-device model execution.
The optimization is typically performed during the graph lowering or ahead-of-time compilation (AOT) phase. A compiler's cost model analyzes the graph to determine if eliminating a subexpression saves more than it costs to store the intermediate result. CSE works synergistically with other passes like constant folding, dead code elimination, and operator fusion within a polyhedral model framework. For sparse model inference, CSE can also optimize the handling of repeated sparse patterns. The primary trade-off involves increased memory for cached values versus reduced compute, making it a key technique in the compression-accuracy tradeoff analysis for deploying models to NPUs and mobile SoCs.
Key Characteristics of CSE
Common Subexpression Elimination (CSE) is a fundamental compiler optimization that improves computational efficiency by identifying and removing redundant calculations. Its effectiveness depends on several core algorithmic properties and trade-offs.
Definition and Core Mechanism
Common Subexpression Elimination (CSE) is a compiler optimization that identifies identical expressions computed multiple times within a program's scope, stores the result of the first computation in a temporary variable, and reuses that value for subsequent occurrences. This transforms the computational graph by:
- Replacing redundant nodes with references to a single, cached computation.
- Reducing total FLOPs (floating-point operations) and kernel launch overhead.
- Increasing data locality by reusing values already in registers or cache.
For example, in an expression like y = (a*b) + c; z = (a*b) * d;, the subexpression (a*b) is computed twice. CSE computes it once, stores it as tmp = a*b, and rewrites the code as y = tmp + c; z = tmp * d;.
Scope Analysis: Local vs. Global
The power of CSE is defined by its scope—the region of code it analyzes for redundancy.
- Local CSE (Basic Block): Operates within a single basic block—a straight-line sequence of code with no branches. It is fast and simple but misses redundancies across different blocks.
- Global CSE: Analyzes an entire function or procedure, tracking expression values across basic blocks and control flow paths (e.g., loops, conditionals). It uses data-flow analysis to determine if an expression is available (already computed and its value is unchanged) at any given program point. This is more powerful but computationally intensive.
- Interprocedural CSE: Extends analysis across function boundaries, potentially eliminating redundancies in different functions. This is rare in production compilers due to complexity and linking issues.
Value Numbering Algorithm
The standard algorithm for implementing CSE is Hash-Based Value Numbering. It assigns a unique value number to each expression based on its operator and the value numbers of its operands.
- The compiler maintains a hash table mapping expression structures to value numbers.
- When encountering an expression like
a + b, it looks up the value numbers foraandb, and the operator+. - If this
(operator, vn_a, vn_b)tuple exists in the table, the expression is redundant and is replaced with the variable holding that value number. - If not, a new value number is assigned, and the entry is added to the table.
This method efficiently identifies structurally identical expressions, even if the original variable names differ (e.g.,
x+yis identical toa+bifxanda,yandbhold the same values).
Interaction with Other Optimizations
CSE is not applied in isolation; it interacts deeply with other compiler passes:
- Constant Folding & Propagation: Creates new opportunities for CSE by replacing variables with constants, making more expressions identical.
- Dead Code Elimination: Must run after CSE to remove the original, now-unused computation if all references to it were replaced.
- Loop-Invariant Code Motion (LICM): Moves invariant expressions outside of loops. CSE can then eliminate redundant instances of those moved expressions.
- Algebraic Simplification: Rewrites
x*1toxora+ato2*a, changing the expression's form and creating new CSE opportunities or invalidating previous matches. - Register Allocation: Benefits from CSE because fewer live variables (the cached results) need to be stored, reducing register pressure.
Trade-offs and Limitations
While powerful, CSE introduces important trade-offs that compilers must balance:
- Increased Register Pressure: Storing temporary results consumes CPU registers. Over-aggressive CSE can cause register spilling to slower memory, negating performance gains.
- Code Size vs. Speed: CSE typically reduces executed instructions but may increase the number of temporary variable assignments, slightly growing code size.
- Side Effects and Aliasing: CSE is unsafe if the redundant expression has side effects (e.g., a function call) or if its operands can be modified between computations (via pointer aliasing). Compilers must perform alias analysis to ensure safety.
- Floating-Point Non-Associativity: For floating-point math, CSE is generally safe and beneficial, as it guarantees bitwise-identical results by reusing the exact computed value, unlike algebraic reassociation.
Application in ML Compilers
In machine learning frameworks like TensorFlow, PyTorch (via TorchScript/TorchDynamo), and Apache TVM, CSE is applied to the computational graph (IR) of neural networks.
- Targets High-Level Operators: Identifies redundant transpose, reshape, or slice operations, or repeated constant tensors used in multiple layers.
- Hardware-Aware Decisions: May be disabled for very small, cheap operations where the cost of storing/loading a temporary exceeds the computation cost.
- Fusion with Constant Folding: Often combined into a single pass that folds constants and eliminates their common subexpressions.
- Example: In a vision transformer, the same positional encoding tensor might be added in multiple residual branches. CSE computes it once and reuses it, saving memory bandwidth and compute.
CSE vs. Related Graph Optimizations
This table compares Common Subexpression Elimination (CSE) with other key compiler optimizations used to transform computational graphs for efficient execution.
| Optimization | Common Subexpression Elimination (CSE) | Constant Folding | Dead Code Elimination | Operator Fusion |
|---|---|---|---|---|
Primary Goal | Eliminate redundant calculations of identical expressions | Evaluate and replace constant expressions at compile-time | Remove operations whose outputs do not affect the final result | Fuse sequential operations into a single kernel |
Scope of Analysis | Local and global within a basic block or function | Local expression trees | Global dataflow analysis across the graph | Local sequences of adjacent operators |
Key Mechanism | Identifies identical subgraphs and caches the result | Pre-computes constant arithmetic/logic | Identifies unused outputs via liveness analysis | Pattern matching and kernel rewriting |
Impact on Graph Size | Reduces number of nodes (eliminates duplicates) | Reduces number of nodes (replaces with constant) | Reduces number of nodes (removes dead ops) | Reduces number of nodes (merges multiple into one) |
Impact on Memory | Reduces intermediate memory for cached results | Eliminates runtime memory for folded constants | Frees memory allocated for dead outputs | Reduces intermediate buffer allocations between fused ops |
Runtime Overhead | Adds minimal overhead for cache lookup/retrieval | Eliminates all runtime computation for folded ops | Eliminates all runtime computation for dead ops | Reduces kernel launch and synchronization overhead |
Applicability to Dynamic Graphs | Limited; requires static expression equivalence | Limited; requires compile-time constants | Yes, but analysis is more complex | Yes, but fusion patterns may be input-dependent |
Typical Performance Gain | 5-20% reduction in compute ops | < 1% to 5% reduction in compute ops | Varies widely; 0% to significant | 10-50% reduction in kernel launch latency & memory traffic |
CSE in AI Frameworks and Compilers
Common subexpression elimination (CSE) is a fundamental compiler optimization that identifies and eliminates redundant calculations of identical expressions within a computational graph, caching and reusing the computed result to improve efficiency.
Core Mechanism
CSE works by analyzing a computational graph's dataflow. It identifies nodes that compute the exact same mathematical expression from the same input tensors. Once identified, the compiler eliminates all but the first computation and rewires the graph so all dependent nodes consume the single cached output. This transforms O(n) redundant work into O(1) work plus a memory read.
- Key Insight: The optimization relies on the deterministic nature of pure computational nodes; the same inputs always produce the same outputs.
- Scope: Can be applied locally within a basic block or globally across an entire function or graph.
Impact on AI Inference
In neural network inference, CSE directly targets and reduces wasteful computation, leading to tangible performance gains.
- Latency Reduction: Eliminates duplicate operations, decreasing end-to-end execution time.
- Memory Bandwidth Savings: By reusing a cached tensor, it reduces the number of reads/writes to high-latency memory (e.g., DRAM), which is often the bottleneck.
- Power Efficiency: Fewer arithmetic operations and memory accesses translate directly into lower energy consumption, a critical metric for edge and mobile deployment.
Example: In a vision transformer, the same sinusoidal positional encoding might be computed multiple times for different attention heads. CSE computes it once and shares the result.
Compiler Integration
CSE is implemented as a pass within modern AI compilers like MLIR, TVM, XLA, and PyTorch's TorchScript/Glow. The pass typically runs after the graph has been canonicalized (put into a standard form) but before hardware-specific lowering.
- Pattern Matching: The compiler traverses the Intermediate Representation (IR), using pattern matching to find identical expression trees.
- Value Numbering: A more advanced technique assigns a unique 'value number' to each computed expression; identical numbers indicate redundancy.
- Graph Rewriting: The IR is rewritten, deleting redundant nodes and redirecting edges.
It often works in concert with passes like constant folding and dead code elimination.
Trade-offs and Considerations
While generally beneficial, applying CSE involves engineering trade-offs that compilers must model.
- Memory vs. Compute: CSE trades compute for memory. The cached result must be stored, potentially increasing the peak memory footprint. The compiler's cost model must decide if the memory pressure is worth the compute savings.
- Fusion Opportunities: An aggressively eliminated subexpression might have been a candidate for operator fusion with a subsequent operation. CSE can sometimes inhibit other optimizations.
- Side Effects: CSE is only safe for pure, side-effect-free operations. It cannot be applied to operations with randomness (e.g., dropout at inference) or stateful reads/writes.
Relation to Other Optimizations
CSE is a member of a family of classical compiler optimizations adapted for computational graphs.
- Constant Folding: A specialized form of CSE where the subexpression consists entirely of constants, allowing the value to be precomputed at compile time.
- Dead Code Elimination: Works synergistically with CSE; after eliminating a redundant calculation, the original node may become dead code if no other consumer exists.
- Common Expression Elimination (CEE): A broader term that may include eliminating similar expressions that can be transformed into a common form, not just identical ones.
- Loop-Invariant Code Motion: A related optimization that hoists identical expressions out of loops, similar to global CSE across loop iterations.
Example in a Computational Graph
Consider a simple graph snippet computing an element-wise operation: Y = (A + B) * C and Z = (A + B) / D.
Before CSE:
codeAdd_1 = A + B Mul_1 = Add_1 * C Add_2 = A + B // Redundant calculation Div_1 = Add_2 / D
After CSE:
codeAdd_1 = A + B // Single computation Mul_1 = Add_1 * C Div_1 = Add_1 / D // Reuses output of Add_1
The second addition (Add_2) is eliminated. The Div_1 operation is rewired to consume the output of Add_1. This saves one arithmetic operation and the associated tensor allocation.
Frequently Asked Questions
Common subexpression elimination (CSE) is a fundamental compiler optimization for neural network inference. These questions address its core mechanisms, benefits, and practical applications in on-device model deployment.
Common subexpression elimination (CSE) is a compiler optimization technique that identifies and eliminates redundant calculations of identical expressions within a computational graph, caching and reusing the computed result to improve execution efficiency.
In the context of a neural network's computational graph, an expression is a sequence of operations that produces a tensor. CSE analyzes the graph to find nodes that perform the exact same computation on the same inputs. When a duplicate is found, the optimizer removes the redundant node and redirects all its dependent nodes to use the output of the first computed instance. This transforms the graph from a structure with duplicate branches into a more compact, directed acyclic graph (DAG). The primary benefit is the reduction of redundant FLOPs (floating-point operations), which directly decreases inference latency and power consumption—critical factors for on-device execution.
Enabling Efficiency, Speed & Accuracy
Intelligent Analysis, Decision & Execution
We build AI systems for teams that need search across company data, workflow automation across tools, or AI features inside products and internal software.
Talk to Us
Search across company data
Give teams answers from docs, tickets, runbooks, and product data with sources and permissions.
Useful when people spend too long searching or get different answers from different systems.

Automate internal workflows
Use AI to route work, draft outputs, trigger actions, and keep approvals and logs in place.
Useful when repetitive work moves across multiple tools and teams.

Add AI to products and internal tools
Build assistants, guided actions, or decision support into the software your team or customers already use.
Useful when AI needs to be part of the product, not a separate tool.
Related Terms
Common Subexpression Elimination is a foundational compiler optimization. These related techniques are often applied in concert within modern ML compilers like TVM, XLA, or ONNX Runtime to transform a high-level computational graph into an efficient, executable program.
Constant Folding
A compile-time optimization that evaluates and replaces expressions consisting entirely of compile-time constants with their precomputed result.
- Key Mechanism: The compiler performs arithmetic at compile time.
- Example: An operation like
y = (2.0 * 3.14) + xis transformed toy = 6.28 + x. - Impact: Eliminates runtime computation for static values, simplifying the graph and reducing kernel launches.
Dead Code Elimination
An optimization pass that identifies and removes operations whose outputs do not affect the final result of the program.
- Key Mechanism: Performs liveness analysis or graph reachability from output nodes.
- Example: Removing a debugging
printoperation or an unused branch of a conditional. - Impact: Reduces execution time, memory usage, and binary size by pruning unnecessary computations.
Operator Fusion
Also known as kernel fusion, this technique combines multiple sequential operations into a single, more efficient kernel.
- Key Mechanism: Fuses ops like Convolution + BatchNorm + ReLU into one compiled kernel.
- Primary Benefit: Drastically reduces intermediate tensor writes to memory (DRAM), which is often the performance bottleneck.
- Use Case: Critical for achieving peak performance on accelerators (GPUs/NPUs) by minimizing memory bandwidth usage.
Common Subexpression Elimination (CSE)
Identifies redundant calculations of identical expressions and replaces them with a reference to a single computed value.
- Key Mechanism: Creates a hash of operation signatures and input tensors to find duplicates.
- Example: If
sin(x)is computed in two separate branches, CSE computes it once and reuses the result. - Impact: Reduces computational work and can decrease memory footprint by reusing buffer allocations.
Peephole Optimization
A low-level compiler technique that examines small sequences of instructions ('peepholes') and replaces them with more efficient equivalents.
- Key Mechanism: Pattern-matches on a localized segment of the IR.
- Examples:
- Replacing
x * 1withx. - Replacing a sequence of
add, mulwith a fused multiply-add (FMA) instruction if supported.
- Replacing
- Impact: Produces tighter, faster machine code by leveraging hardware-specific idioms.
Static Memory Planning
A compile-time optimization that pre-allocates and reuses memory buffers for tensors by analyzing their lifetimes in the computational graph.
- Key Mechanism: Performs liveness analysis to determine when tensors are first used and last needed.
- Benefit: Minimizes dynamic memory allocation overhead and reduces the peak memory footprint, which is essential for memory-constrained edge devices.
- Outcome: Creates a static memory plan where buffers are reused for non-overlapping tensors.

About the author
Prasad Kumkar
CEO & MD, Inference Systems
Prasad Kumkar is the CEO & MD of Inference Systems and writes about AI systems architecture, LLM infrastructure, model serving, evaluation, and production deployment. Over 5+ years, he has worked across computer vision models, L5 autonomous vehicle systems, and LLM research, with a focus on taking complex AI ideas into real-world engineering systems.
His work and writing cover AI systems, large language models, AI agents, multimodal systems, autonomous systems, inference optimization, RAG, evaluation, and production AI engineering.
Partnered with leading AI, data, and software stack.
How We Work
Custom AI workflows for your Business
One-fit-all AI don't work for modern businesses. At Inferensys, we aim to understand your business & custom requirements; which we use to define most efficient agentic workflows, the data, and the tools for your business.
01
Review the use case
We understand the task, the users, and where AI can actually help.
Read more02
Pick the right approach
We define what needs search, automation, or product integration.
Read more03
Build the first useful version
We implement the part that proves the value first.
Read more04
Improve from there
We add the checks and visibility needed to keep it useful.
Read moreThe first call is a practical review of your use case and the right next step.
Talk to Us