Skip to content

Commit a14e03e

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 32b0d0a commit a14e03e

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
@@ -37,6 +37,7 @@
3737
import com.hubspot.jinjava.lib.tag.TagLibrary;
3838
import com.hubspot.jinjava.lib.tag.eager.DeferredToken;
3939
import com.hubspot.jinjava.mode.EagerExecutionMode;
40+
import com.hubspot.jinjava.tree.ExpressionNode;
4041
import com.hubspot.jinjava.tree.Node;
4142
import com.hubspot.jinjava.util.DeferredValueUtils;
4243
import com.hubspot.jinjava.util.ScopeMap;
@@ -424,6 +425,10 @@ public Set<Node> getDeferredNodes() {
424425
return ImmutableSet.copyOf(deferredNodes);
425426
}
426427

428+
public long countNonExpressionDeferredNodes() {
429+
return deferredNodes.stream().filter(n -> !(n instanceof ExpressionNode)).count();
430+
}
431+
427432
@Beta
428433
public void checkNumberOfDeferredTokens() {
429434
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
@@ -32,7 +32,6 @@
3232
import com.hubspot.jinjava.interpret.TemplateSyntaxException;
3333
import com.hubspot.jinjava.objects.DummyObject;
3434
import com.hubspot.jinjava.objects.collections.PyList;
35-
import com.hubspot.jinjava.tree.ExpressionNode;
3635
import com.hubspot.jinjava.tree.Node;
3736
import com.hubspot.jinjava.tree.TagNode;
3837
import com.hubspot.jinjava.tree.parse.TagToken;
@@ -111,20 +110,11 @@ public boolean isRenderedInValidationMode() {
111110
public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) {
112111
long numDeferredNodesBefore = interpreter
113112
.getContext()
114-
.getDeferredNodes()
115-
.stream()
116-
.filter(n -> !(n instanceof ExpressionNode))
117-
.count();
113+
.countNonExpressionDeferredNodes();
118114

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

0 commit comments

Comments
 (0)