1111import org .junit .Test ;
1212
1313/**
14- * Performance test to verify that the optimized filter chain performs better than
15- * the nested method approach.
14+ * Performance tests for the filter chain optimization.
1615 *
17- * Run with: mvn test -Dtest=AstFilterChainPerformanceTest
18- * Or run the main() method directly for more detailed output.
16+ * Run manually with: mvn test -Dtest=AstFilterChainPerformanceTest
17+ * Or run the main() method directly for detailed output.
1918 */
2019public class AstFilterChainPerformanceTest {
2120
@@ -62,42 +61,69 @@ public void runPerformanceComparison() {
6261 System .out .println ("=== Filter Chain Performance Test ===\n " );
6362 System .out .println ("Warming up..." );
6463
65- // Warmup
66- runFilterTests (jinjavaOptimized , warmupIterations , false );
67- runFilterTests (jinjavaUnoptimized , warmupIterations , false );
64+ runFilterTests (jinjavaOptimized , warmupIterations );
65+ runFilterTests (jinjavaUnoptimized , warmupIterations );
6866
6967 System .out .println (
7068 "Running performance tests with " + testIterations + " iterations each\n "
7169 );
7270
73- // Single filter
7471 comparePerformance ("Single filter: {{ name|trim }}" , testIterations );
75-
76- // Two chained filters
7772 comparePerformance ("Two filters: {{ name|trim|lower }}" , testIterations );
78-
79- // Three chained filters
8073 comparePerformance ("Three filters: {{ name|trim|lower|capitalize }}" , testIterations );
81-
82- // Five chained filters
8374 comparePerformance (
8475 "Five filters: {{ text|upper|replace('THE', 'a')|trim|lower|title }}" ,
8576 testIterations
8677 );
87-
88- // Filter with arguments
8978 comparePerformance (
9079 "Filters with args: {{ text|truncate(20)|upper }}" ,
9180 testIterations
9281 );
93-
94- // Multiple filter chains in same template
9582 comparePerformance (
9683 "Multiple chains: {{ name|trim|lower }} and {{ text|upper|truncate(10) }}" ,
9784 testIterations
9885 );
9986 }
10087
88+ @ Test
89+ public void optimizedVersionShouldBeFaster () {
90+ int warmupIterations = 100 ;
91+ int testIterations = 1000 ;
92+ String template = "{{ content.text|upper|replace('THE', 'a')|trim|lower|title }}" ;
93+
94+ for (int i = 0 ; i < warmupIterations ; i ++) {
95+ jinjavaOptimized .render (template , context );
96+ jinjavaUnoptimized .render (template , context );
97+ }
98+
99+ long totalOptimizedTime = 0 ;
100+ long totalUnoptimizedTime = 0 ;
101+ int rounds = 3 ;
102+
103+ for (int round = 0 ; round < rounds ; round ++) {
104+ totalUnoptimizedTime += timeExecution (jinjavaUnoptimized , template , testIterations );
105+ totalOptimizedTime += timeExecution (jinjavaOptimized , template , testIterations );
106+ }
107+
108+ long avgUnoptimizedTime = totalUnoptimizedTime / rounds ;
109+ long avgOptimizedTime = totalOptimizedTime / rounds ;
110+
111+ System .out .printf (
112+ "Performance test: Optimized=%d ms, Unoptimized=%d ms, Speedup=%.2fx%n" ,
113+ avgOptimizedTime ,
114+ avgUnoptimizedTime ,
115+ (1.0 * avgUnoptimizedTime ) / avgOptimizedTime
116+ );
117+
118+ assertThat (avgOptimizedTime )
119+ .as (
120+ "Optimized (%d ms) should be faster than unoptimized (%d ms)" ,
121+ avgOptimizedTime ,
122+ avgUnoptimizedTime
123+ )
124+ .isLessThan ((avgUnoptimizedTime * 95 ) / 100 );
125+ }
126+
101127 private void comparePerformance (String description , int iterations ) {
102128 String template = description .substring (description .indexOf ("{{" ));
103129 if (description .contains (":" )) {
@@ -106,13 +132,10 @@ private void comparePerformance(String description, int iterations) {
106132
107133 System .out .println (description );
108134
109- // Run optimized
110135 long optimizedTime = timeExecution (jinjavaOptimized , template , iterations );
111-
112- // Run unoptimized
113136 long unoptimizedTime = timeExecution (jinjavaUnoptimized , template , iterations );
114137
115- double speedup = (double ) unoptimizedTime / optimizedTime ;
138+ double speedup = (1.0 * unoptimizedTime ) / optimizedTime ;
116139 System .out .printf (
117140 " Optimized: %d ms, Unoptimized: %d ms, Speedup: %.2fx%n%n" ,
118141 optimizedTime ,
@@ -129,7 +152,7 @@ private long timeExecution(Jinjava jinjava, String template, int iterations) {
129152 return System .currentTimeMillis () - startTime ;
130153 }
131154
132- private void runFilterTests (Jinjava jinjava , int iterations , boolean print ) {
155+ private void runFilterTests (Jinjava jinjava , int iterations ) {
133156 String [] templates = {
134157 "{{ name|trim }}" ,
135158 "{{ name|trim|lower }}" ,
@@ -144,101 +167,4 @@ private void runFilterTests(Jinjava jinjava, int iterations, boolean print) {
144167 }
145168 }
146169 }
147-
148- @ Test
149- public void itProducesSameResultsWithAndWithoutOptimization () {
150- String [] templates = {
151- "{{ name|trim }}" ,
152- "{{ name|trim|lower }}" ,
153- "{{ name|trim|lower|capitalize }}" ,
154- "{{ text|upper|replace('THE', 'a')|trim|lower|title }}" ,
155- "{{ text|truncate(20)|upper }}" ,
156- "{{ name|trim|lower }} and {{ text|upper|truncate(10) }}" ,
157- "{{ items|join(', ')|upper }}" ,
158- "{{ number|string|length }}" ,
159- };
160-
161- for (String template : templates ) {
162- String optimizedResult = jinjavaOptimized .render (template , context );
163- String unoptimizedResult = jinjavaUnoptimized .render (template , context );
164- assertThat (optimizedResult )
165- .as ("Template: " + template )
166- .isEqualTo (unoptimizedResult );
167- }
168- }
169-
170- @ Test
171- public void itHandlesSingleFilterWithOptimization () {
172- String result = jinjavaOptimized .render ("{{ name|trim }}" , context );
173- assertThat (result ).isEqualTo ("Hello World" );
174- }
175-
176- @ Test
177- public void itHandlesChainedFiltersWithOptimization () {
178- String result = jinjavaOptimized .render ("{{ name|trim|lower }}" , context );
179- assertThat (result ).isEqualTo ("hello world" );
180- }
181-
182- @ Test
183- public void itHandlesFiltersWithArgumentsWithOptimization () {
184- String result = jinjavaOptimized .render ("{{ text|truncate(20)|upper }}" , context );
185- assertThat (result ).isNotEmpty ();
186- assertThat (result ).isUpperCase ();
187- }
188-
189- @ Test
190- public void itHandlesComplexFilterChainWithOptimization () {
191- String result = jinjavaOptimized .render (
192- "{{ text|upper|replace('THE', 'a')|trim|lower|capitalize }}" ,
193- context
194- );
195- assertThat (result ).isNotEmpty ();
196- }
197-
198- /**
199- * This test verifies that the optimized version is faster than the unoptimized version.
200- * The optimization should provide a measurable speedup for chained filters.
201- */
202- @ Test
203- public void optimizedVersionShouldBeFaster () {
204- int warmupIterations = 100 ;
205- int testIterations = 1000 ;
206- String template = "{{ content.text|upper|replace('THE', 'a')|trim|lower|title }}" ;
207-
208- // Warmup both to ensure JIT compilation
209- for (int i = 0 ; i < warmupIterations ; i ++) {
210- jinjavaOptimized .render (template , context );
211- jinjavaUnoptimized .render (template , context );
212- }
213-
214- // Run multiple rounds to get more stable results
215- long totalOptimizedTime = 0 ;
216- long totalUnoptimizedTime = 0 ;
217- int rounds = 3 ;
218-
219- for (int round = 0 ; round < rounds ; round ++) {
220- totalUnoptimizedTime += timeExecution (jinjavaUnoptimized , template , testIterations );
221- totalOptimizedTime += timeExecution (jinjavaOptimized , template , testIterations );
222- }
223-
224- long avgUnoptimizedTime = totalUnoptimizedTime / rounds ;
225- long avgOptimizedTime = totalOptimizedTime / rounds ;
226-
227- System .out .printf (
228- "Performance test: Optimized=%d ms, Unoptimized=%d ms, Speedup=%.2fx%n" ,
229- avgOptimizedTime ,
230- avgUnoptimizedTime ,
231- (double ) avgUnoptimizedTime / avgOptimizedTime
232- );
233-
234- // The optimized version should be faster (allow 10% margin for system variance)
235- // If optimized takes more than 90% of unoptimized time, fail the test
236- assertThat (avgOptimizedTime )
237- .as (
238- "Optimized (%d ms) should be faster than unoptimized (%d ms)" ,
239- avgOptimizedTime ,
240- avgUnoptimizedTime
241- )
242- .isLessThan ((long ) (avgUnoptimizedTime * 0.95 ));
243- }
244170}
0 commit comments