Skip to content

Commit e2275c1

Browse files
jasmith-hsclaude
andcommitted
Avoid ImmutableSet.copyOf in ForTag deferred node counting
ForTag was calling getDeferredNodes() which creates an ImmutableSet copy on every iteration just to count non-expression nodes. Added a dedicated countNonExpressionDeferredNodes() method on Context that streams over the backing set directly, avoiding the copy. Benchmark: +66% throughput on complexTemplateBenchmark (3,316 -> 5,495 ops/s) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 47695db commit e2275c1

2 files changed

Lines changed: 7 additions & 12 deletions

File tree

src/main/java/com/hubspot/jinjava/interpret/Context.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.hubspot.jinjava.lib.tag.TagLibrary;
3939
import com.hubspot.jinjava.lib.tag.eager.DeferredToken;
4040
import com.hubspot.jinjava.mode.EagerExecutionMode;
41+
import com.hubspot.jinjava.tree.ExpressionNode;
4142
import com.hubspot.jinjava.tree.Node;
4243
import com.hubspot.jinjava.util.DeferredValueUtils;
4344
import com.hubspot.jinjava.util.ScopeMap;
@@ -425,6 +426,10 @@ public Set<Node> getDeferredNodes() {
425426
return ImmutableSet.copyOf(deferredNodes);
426427
}
427428

429+
public long countNonExpressionDeferredNodes() {
430+
return deferredNodes.stream().filter(n -> !(n instanceof ExpressionNode)).count();
431+
}
432+
428433
@Beta
429434
public void checkNumberOfDeferredTokens() {
430435
Context secondToLastContext = this;

src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import com.hubspot.jinjava.interpret.TemplateSyntaxException;
3434
import com.hubspot.jinjava.objects.DummyObject;
3535
import com.hubspot.jinjava.objects.collections.PyList;
36-
import com.hubspot.jinjava.tree.ExpressionNode;
3736
import com.hubspot.jinjava.tree.Node;
3837
import com.hubspot.jinjava.tree.TagNode;
3938
import com.hubspot.jinjava.tree.parse.TagToken;
@@ -112,20 +111,11 @@ public boolean isRenderedInValidationMode() {
112111
public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) {
113112
long numDeferredNodesBefore = interpreter
114113
.getContext()
115-
.getDeferredNodes()
116-
.stream()
117-
.filter(n -> !(n instanceof ExpressionNode))
118-
.count();
114+
.countNonExpressionDeferredNodes();
119115

120116
String result = interpretUnchecked(tagNode, interpreter);
121117
if (
122-
interpreter
123-
.getContext()
124-
.getDeferredNodes()
125-
.stream()
126-
.filter(n -> !(n instanceof ExpressionNode))
127-
.count() >
128-
numDeferredNodesBefore
118+
interpreter.getContext().countNonExpressionDeferredNodes() > numDeferredNodesBefore
129119
) {
130120
throw new DeferredValueException(
131121
"for loop",

0 commit comments

Comments
 (0)