Inferensys

Glossary

Common Subexpression Elimination (CSE)

Common Subexpression Elimination (CSE) is a compiler optimization that identifies redundant computations of identical expressions and replaces them with a single stored value to avoid repeated calculation.
Stylish WeWork-like workspace with hot desks and document wall, professional searching through enterprise knowledge base on a mounted ultrawide display, warm industrial pendants overhead.
GRAPH COMPILATION STRATEGIES

What is Common Subexpression Elimination (CSE)?

A foundational compiler optimization for computational graphs that directly reduces redundant arithmetic operations.

Common Subexpression Elimination (CSE) is a compiler optimization that identifies and eliminates redundant calculations of identical expressions within a program or computational graph, replacing them with a single computed value stored in a temporary variable. In the context of neural network compilation, CSE analyzes the dataflow graph to find operations—like a repeated matrix multiplication or activation function on the same input tensors—that produce identical results, removing the duplicate computations. This reduces execution time and computational load, which is critical for efficiency on hardware accelerators like Neural Processing Units (NPUs).

The optimization is performed during the graph compilation phase, often on an Intermediate Representation (IR). It relies on precise dataflow analysis to ensure the eliminated expression is truly redundant and its value hasn't changed. CSE works synergistically with other passes like constant folding and dead code elimination. For NPU targets, reducing operations through CSE decreases kernel launch overhead and improves memory hierarchy utilization by minimizing redundant data accesses, directly contributing to lower latency and improved power efficiency in AI workloads.

GRAPH COMPILATION STRATEGIES

Key Characteristics of CSE

Common Subexpression Elimination (CSE) is a fundamental compiler optimization that identifies and eliminates redundant calculations within a computational graph. Its primary goal is to reduce execution time and computational overhead by caching and reusing results.

01

Core Optimization Principle

CSE operates on the principle of computational redundancy. It scans the computational graph to find subexpressions—sequences of operations—that produce identical results. When a duplicate is found, the compiler replaces all subsequent instances with a reference to the first computed value. This transforms the graph from a structure with repeated calculations into one with shared intermediate results, directly reducing the number of floating-point operations (FLOPs) executed at runtime.

02

Scope and Granularity

The effectiveness of CSE depends on its scope of analysis:

  • Local CSE: Operates within a single basic block or a small, contiguous sequence of operations. It is fast and common in early optimization passes.
  • Global CSE: Analyzes the entire function or computational graph, tracking values across control flow boundaries (like loops and conditionals). This is more powerful but requires sophisticated dataflow analysis.
  • Inter-Procedural CSE: Extends analysis across function boundaries, potentially inlining functions to expose more redundancy. This is complex and often used in link-time optimization (LTO).
03

Interaction with Graph Representation

CSE is heavily influenced by the graph's Intermediate Representation (IR). In an SSA (Static Single Assignment) form, where each variable is assigned once, value identity is explicit, making CSE more straightforward. The compiler often performs CSE after graph canonicalization, which standardizes operator forms (e.g., rewriting (a + b) and (b + a) as identical if addition is commutative), ensuring maximum redundancy is detected. It is a prerequisite for many subsequent optimizations like constant folding and dead code elimination.

04

Trade-offs and Considerations

While primarily beneficial, CSE introduces trade-offs that compilers must balance:

  • Increased Memory Pressure: Storing a computed value for reuse consumes register or stack memory. Aggressive CSE can increase register pressure, potentially causing spills to slower memory.
  • Instruction Scheduling Impact: Creating a single, shared computation point may lengthen a critical path if the value is needed early by multiple consumers.
  • Side Effects and Purity: CSE can only be applied to pure functions—operations without side effects (e.g., sin(x), a + b). It cannot be applied to stateful operations like random number generation or memory writes.
05

NPU-Specific Implications

For Neural Processing Units (NPUs), CSE is critical but interacts uniquely with hardware:

  • Fusion Opportunities: Eliminating a redundant subgraph may remove a candidate for kernel fusion, as the operations are no longer adjacent. The compiler must evaluate the cost-benefit of CSE vs. fusion.
  • Precision and Rounding: On hardware using mixed-precision (e.g., FP16, INT8), reusing a value stored in a lower-precision register may propagate rounding errors differently than recomputing in higher precision.
  • Scheduling for Parallelism: On massively parallel NPUs, recomputing a value in two separate processing elements (PEs) might be faster than broadcasting it from a single source, depending on data movement costs.
06

Example: Mathematical Expression

Consider the expression y = (a*b) + (a*b) + c. A naive computation performs (a*b) twice.

Original Computation Graph: MUL1 = a * b MUL2 = a * b ADD1 = MUL1 + MUL2 y = ADD1 + c

After CSE: MUL1 = a * b // The single, common subexpression ADD1 = MUL1 + MUL1 // Reuse the result y = ADD1 + c

The optimization reduces one MUL operation. In a neural network, this pattern occurs with repeated weight multiplications or activation function applications across identical tensor slices.

COMPARISON

CSE vs. Related Compiler Optimizations

This table distinguishes Common Subexpression Elimination from other key compiler optimizations used in graph compilation for NPUs, highlighting their primary objectives, scopes, and typical application points.

OptimizationPrimary ObjectiveScope of AnalysisTypical Application PointKey Interaction with CSE

Common Subexpression Elimination (CSE)

Eliminate redundant calculation of identical expressions

Local basic block or global dataflow

Mid-level IR, after canonicalization

Core technique; often a prerequisite for others

Constant Folding

Evaluate constant expressions at compile time

Expression trees with compile-time constants

Early IR passes, often before CSE

Creates new constants that CSE can then identify and eliminate

Dead Code Elimination (DCE)

Remove code that does not affect program output

Dataflow and control-flow graph

Multiple passes, often after CSE and constant propagation

Removes code made dead by CSE's substitution

Loop-Invariant Code Motion (LICM)

Hoist invariant computations out of loops

Loop bodies and their dominators

Loop optimization phase

Moves expressions to loop preheader, enabling global CSE across iterations

Copy Propagation

Replace uses of a variable with its directly assigned value

Def-use chains within SSA form

Mid-level optimization, often alongside CSE

Simplifies expressions, potentially creating new common subexpressions for CSE to find

Peephole Optimization

Replace short instruction sequences with more efficient ones

A small window of adjacent instructions

Low-level IR or assembly, late in the pipeline

Operates on a lower-level representation than typical CSE; can clean up after CSE

Global Value Numbering (GVN)

Detect and eliminate redundant computations (a form of CSE)

Entire function or region using value equivalence

Mid-level optimization, similar scope to global CSE

A more powerful, value-based algorithm that subsumes syntactic CSE

Partial Redundancy Elimination (PRE)

Move computations to paths where they are fully redundant to enable elimination

Control-flow graph paths

Advanced optimization pass

Generalizes CSE and LICM to handle partially redundant expressions

GRAPH COMPILATION STRATEGIES

Frequently Asked Questions

Common subexpression elimination (CSE) is a foundational compiler optimization crucial for efficient neural network execution on NPUs. These questions address its core mechanisms, benefits, and role within the compilation pipeline.

Common subexpression elimination (CSE) is a compiler optimization that identifies and eliminates redundant calculations of identical expressions within a computational graph, replacing them with a single stored value. It works by analyzing the dataflow graph of a program, such as a neural network, to find expressions that are computed more than once with the same inputs. When a duplicate is found, the compiler computes the expression once, stores the result in a temporary variable, and replaces all subsequent instances with a reference to that variable. This process reduces computational overhead and can decrease memory traffic by avoiding repeated computation of the same intermediate tensor.

For example, in a graph where the operation y = (a + b) * c appears in two separate branches, CSE would compute (a + b) once, store it as a temporary tmp1, and rewrite the graph to y = tmp1 * c in both locations.

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.