Inferensys

Glossary

Common Subexpression Elimination

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 efficiency.
Performance engineer optimizing AI latency on laptop, latency charts visible, technical optimization session.
COMPUTE GRAPH OPTIMIZATION

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.

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.

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.

COMPUTE GRAPH OPTIMIZATION

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.

01

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;.

02

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.
03

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 for a and b, 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+y is identical to a+b if x and a, y and b hold the same values).
04

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*1 to x or a+a to 2*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.
05

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.
06

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.
COMPARISON

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.

OptimizationCommon Subexpression Elimination (CSE)Constant FoldingDead Code EliminationOperator 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

COMPUTE GRAPH OPTIMIZATION

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.

01

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.
02

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.

03

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.

  1. Pattern Matching: The compiler traverses the Intermediate Representation (IR), using pattern matching to find identical expression trees.
  2. Value Numbering: A more advanced technique assigns a unique 'value number' to each computed expression; identical numbers indicate redundancy.
  3. 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.

04

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.
05

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.
06

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:

code
    Add_1 = A + B
    Mul_1 = Add_1 * C
    Add_2 = A + B  // Redundant calculation
    Div_1 = Add_2 / D

After CSE:

code
    Add_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.

COMPUTE GRAPH OPTIMIZATION

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.

Prasad Kumkar

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.