Skip to content

Commit 95094de

Browse files
committed
Add documentation for filter chain optimization
1 parent 63ff83c commit 95094de

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

filter-chain-optimization.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Filter Chain Optimization
2+
3+
## Overview
4+
5+
This branch introduces a performance optimization for chained filter expressions in Jinjava's expression language. The optimization is **configurable** and **disabled by default** for backward compatibility.
6+
7+
## Problem
8+
9+
Previously, chained filters like `input|trim|lower|length` were parsed as deeply nested AST method calls:
10+
11+
```
12+
filter:length.filter(filter:lower.filter(filter:trim.filter(input, interpreter), interpreter), interpreter)
13+
```
14+
15+
This resulted in:
16+
- Multiple redundant filter lookups per AST node traversal
17+
- Increased method invocation overhead
18+
- Extra object wrapping/unwrapping between filters
19+
- Unnecessary context operations
20+
21+
## Solution
22+
23+
The optimization introduces a new `AstFilterChain` AST node that represents the entire filter chain as a **single evaluation unit**:
24+
25+
```
26+
input|trim|lower|length
27+
```
28+
29+
Instead of nested method calls, the filter chain is evaluated in a single pass:
30+
1. Look up each filter once
31+
2. Directly invoke `filter.filter(...)` sequentially
32+
3. Handle `SafeString` preservation inline
33+
4. Handle disabled filters and errors
34+
35+
## New Files
36+
37+
| File | Description |
38+
|------|-------------|
39+
| `AstFilterChain.java` | AST node for optimized filter chain evaluation |
40+
| `FilterSpec.java` | Data class holding filter name and parameters |
41+
| `AstFilterChainPerformanceTest.java` | Performance tests verifying the optimization |
42+
43+
## Modified Files
44+
45+
| File | Changes |
46+
|------|---------|
47+
| `JinjavaConfig.java` | Added `enableFilterChainOptimization` config option |
48+
| `ExtendedParser.java` | Added conditional logic to use optimization based on config |
49+
| `EagerExtendedParser.java` | Override to always use old behavior (no optimization for eager mode) |
50+
51+
## Configuration
52+
53+
To enable the filter chain optimization:
54+
55+
```java
56+
JinjavaConfig config = JinjavaConfig.newBuilder()
57+
.withEnableFilterChainOptimization(true)
58+
.build();
59+
60+
Jinjava jinjava = new Jinjava(config);
61+
```
62+
63+
### Behavior by Execution Mode
64+
65+
| Mode | Config Enabled | Behavior |
66+
|------|---------------|----------|
67+
| Normal (non-eager) | `true` | Uses optimized `AstFilterChain` |
68+
| Normal (non-eager) | `false` | Uses old nested method approach |
69+
| Eager | `true` or `false` | Always uses old nested method approach |
70+
71+
**Note:** The optimization is disabled for eager execution mode regardless of configuration, as eager mode requires the old behavior for proper deferred value handling.
72+
73+
## Performance
74+
75+
The optimization provides measurable performance improvements for chained filter expressions:
76+
77+
- **Single filter**: Minimal improvement
78+
- **2+ chained filters**: Noticeable improvement
79+
- **5+ chained filters**: Significant improvement
80+
81+
Run the performance test to see actual numbers:
82+
83+
```bash
84+
# Run automated performance test
85+
mvn test -Dtest=AstFilterChainPerformanceTest
86+
87+
# Run detailed benchmark (main method)
88+
mvn exec:java -Dexec.mainClass="com.hubspot.jinjava.el.ext.AstFilterChainPerformanceTest"
89+
```
90+
91+
## Commits
92+
93+
1. **Chained filters optimization** - Initial implementation with `AstFilterChain` and `FilterSpec`
94+
2. **Make filter chain optimization configurable and disable for eager mode** - Added config option and disabled for eager execution
95+
3. **Add performance test for filter chain optimization** - Added tests to verify correctness and performance
96+
97+
## Backward Compatibility
98+
99+
- The optimization is **disabled by default** (`enableFilterChainOptimization = false`)
100+
- Existing code works unchanged without any configuration
101+
- Eager execution mode always uses the old behavior
102+
- All existing tests pass without modification

0 commit comments

Comments
 (0)