Skip to content
This repository was archived by the owner on May 10, 2024. It is now read-only.

Commit 1c49e54

Browse files
committed
dcm: optimization pass to evaluate constant sub-query expressions only once
Signed-off-by: Lalith Suresh <lsuresh@vmware.com>
1 parent 8071541 commit 1c49e54

File tree

4 files changed

+79
-72
lines changed

4 files changed

+79
-72
lines changed

dcm/src/main/java/com/vmware/dcm/backend/ortools/GetColumnIdentifiers.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,26 @@
66

77
package com.vmware.dcm.backend.ortools;
88

9-
import com.vmware.dcm.compiler.monoid.VoidType;
109
import com.vmware.dcm.compiler.monoid.ColumnIdentifier;
1110
import com.vmware.dcm.compiler.monoid.GroupByComprehension;
1211
import com.vmware.dcm.compiler.monoid.MonoidComprehension;
1312
import com.vmware.dcm.compiler.monoid.SimpleVisitor;
13+
import com.vmware.dcm.compiler.monoid.VoidType;
1414

1515
import java.util.LinkedHashSet;
1616

1717

1818
/**
19-
* A visitor that returns the set of accessed columns within a comprehension's scope, *without entering
19+
* A visitor that returns the set of accessed columns within a comprehension's scope, without entering
2020
* sub-queries.
2121
*/
2222
class GetColumnIdentifiers extends SimpleVisitor {
2323
private final LinkedHashSet<ColumnIdentifier> columnIdentifiers = new LinkedHashSet<>();
24+
private final boolean visitInnerComprehensions;
25+
26+
GetColumnIdentifiers(final boolean visitInnerComprehensions) {
27+
this.visitInnerComprehensions = visitInnerComprehensions;
28+
}
2429

2530
@Override
2631
protected VoidType visitColumnIdentifier(final ColumnIdentifier node, final VoidType context) {
@@ -30,11 +35,17 @@ protected VoidType visitColumnIdentifier(final ColumnIdentifier node, final Void
3035

3136
@Override
3237
protected VoidType visitMonoidComprehension(final MonoidComprehension node, final VoidType context) {
38+
if (visitInnerComprehensions) {
39+
super.visitMonoidComprehension(node, context);
40+
}
3341
return defaultReturn();
3442
}
3543

3644
@Override
3745
protected VoidType visitGroupByComprehension(final GroupByComprehension node, final VoidType context) {
46+
if (visitInnerComprehensions) {
47+
super.visitGroupByComprehension(node, context);
48+
}
3849
return defaultReturn();
3950
}
4051

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2018-2020 VMware, Inc. All Rights Reserved.
3+
* SPDX-License-Identifier: BSD-2
4+
*/
5+
6+
package com.vmware.dcm.backend.ortools;
7+
8+
import com.vmware.dcm.compiler.monoid.ColumnIdentifier;
9+
import com.vmware.dcm.compiler.monoid.GroupByComprehension;
10+
import com.vmware.dcm.compiler.monoid.MonoidComprehension;
11+
import com.vmware.dcm.compiler.monoid.TableRowGenerator;
12+
13+
import java.util.LinkedHashSet;
14+
import java.util.Set;
15+
import java.util.stream.Collectors;
16+
17+
/*
18+
* Evaluates whether a sub-query (and its inner sub-queries etc.) can be treated as a constant expression.
19+
*/
20+
public class IsConstantSubquery {
21+
22+
static boolean apply(final MonoidComprehension expr) {
23+
final GetColumnIdentifiers visitor = new GetColumnIdentifiers(true);
24+
if (expr instanceof GroupByComprehension) {
25+
final MonoidComprehension comprehension = ((GroupByComprehension) expr).getComprehension();
26+
comprehension.getHead().getSelectExprs().forEach(visitor::visit);
27+
comprehension.getQualifiers().forEach(visitor::visit);
28+
} else {
29+
expr.getHead().getSelectExprs().forEach(visitor::visit);
30+
expr.getQualifiers().forEach(visitor::visit);
31+
}
32+
final LinkedHashSet<ColumnIdentifier> columnIdentifiers = visitor.getColumnIdentifiers();
33+
34+
final Set<String> accessedTables = expr.getQualifiers()
35+
.stream().filter(q -> q instanceof TableRowGenerator)
36+
.map(e -> ((TableRowGenerator ) e).getTable().getAliasedName())
37+
.collect(Collectors.toSet());
38+
return columnIdentifiers.stream().allMatch(
39+
ci -> !ci.getField().isControllable() && accessedTables.contains(ci.getTableName())
40+
);
41+
}
42+
}

dcm/src/main/java/com/vmware/dcm/backend/ortools/OrToolsSolver.java

Lines changed: 23 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,9 @@
8080
import java.net.URLClassLoader;
8181
import java.nio.file.Files;
8282
import java.nio.file.Path;
83-
import java.util.ArrayDeque;
8483
import java.util.ArrayList;
8584
import java.util.Collection;
8685
import java.util.Collections;
87-
import java.util.Deque;
8886
import java.util.HashMap;
8987
import java.util.HashSet;
9088
import java.util.LinkedHashSet;
@@ -265,7 +263,7 @@ public List<String> generateModelCode(final IRContext context,
265263
final OutputIR.Block outerBlock = outputIR.newBlock("outer");
266264
translationContext.enterScope(outerBlock);
267265
final OutputIR.Block block = addView(name, rewrittenComprehension, false, translationContext);
268-
translationContext.leaveScope();
266+
output.addCode(translationContext.leaveScope().toString());
269267
output.addCode(block.toString());
270268
});
271269
constraintViews
@@ -277,7 +275,7 @@ public List<String> generateModelCode(final IRContext context,
277275
final OutputIR.Block outerBlock = outputIR.newBlock("outer");
278276
translationContext.enterScope(outerBlock);
279277
final OutputIR.Block block = addView(name, rewrittenComprehension, true, translationContext);
280-
translationContext.leaveScope();
278+
output.addCode(translationContext.leaveScope().toString());
281279
output.addCode(block.toString());
282280
} else {
283281
final OutputIR.Block outerBlock = outputIR.newBlock("outer");
@@ -297,7 +295,6 @@ public List<String> generateModelCode(final IRContext context,
297295
final OutputIR.Block outerBlock = outputIR.newBlock("outer");
298296
objFunctionContext.enterScope(outerBlock);
299297
final String exprStr = exprToStr(rewrittenComprehension, objFunctionContext);
300-
objFunctionContext.leaveScope();
301298
output.addCode(outerBlock.toString());
302299
output.addStatement("final $T $L = $L", IntVar.class, name, exprStr);
303300
});
@@ -568,7 +565,7 @@ private List<ColumnIdentifier> getColumnsAccessed(final List<? extends Expr> exp
568565
}
569566

570567
private LinkedHashSet<ColumnIdentifier> getColumnsAccessed(final Expr expr) {
571-
final GetColumnIdentifiers visitor = new GetColumnIdentifiers();
568+
final GetColumnIdentifiers visitor = new GetColumnIdentifiers(false);
572569
visitor.visit(expr);
573570
return visitor.getColumnIdentifiers();
574571
}
@@ -1590,12 +1587,12 @@ protected String visitMonoidComprehension(final MonoidComprehension node, final
15901587
visitor.visit(headSelectItem);
15911588
final boolean headSelectItemContainsMonoidFunction = visitor.getFound();
15921589

1593-
final OutputIR.Block currentBlock = context.currentScope();
1590+
attemptConstantSubqueryOptimization(node, subQueryBlock, context);
1591+
15941592
final TranslationContext newCtx = context.withEnterFunctionContext();
15951593
// If the head contains a function, then this is a scalar subquery
15961594
if (headSelectItemContainsMonoidFunction) {
15971595
newCtx.enterScope(subQueryBlock);
1598-
currentBlock.addBody(subQueryBlock);
15991596
final String ret = apply(innerVisitor.visit(headSelectItem, newCtx), context);
16001597
newCtx.leaveScope();
16011598
return ret;
@@ -1606,7 +1603,6 @@ protected String visitMonoidComprehension(final MonoidComprehension node, final
16061603
final String type = inferType(node.getHead().getSelectExprs().get(0));
16071604
final String listName =
16081605
extractListFromLoop(processedHeadItem, subQueryBlock, newSubqueryName, type);
1609-
currentBlock.addBody(subQueryBlock);
16101606
newCtx.leaveScope();
16111607
return apply(listName, subQueryBlock, context);
16121608
}
@@ -1624,12 +1620,13 @@ protected String visitGroupByComprehension(final GroupByComprehension node, fina
16241620
Preconditions.checkArgument(node.getComprehension().getHead().getSelectExprs().size() == 1);
16251621
final Expr headSelectItem = node.getComprehension().getHead().getSelectExprs().get(0);
16261622

1627-
final OutputIR.Block currentBlock = context.currentScope();
16281623
final TranslationContext newCtx = context.withEnterFunctionContext();
1624+
1625+
attemptConstantSubqueryOptimization(node, subQueryBlock, context);
1626+
16291627
// if scalar subquery
16301628
if (headSelectItem instanceof MonoidFunction) {
16311629
newCtx.enterScope(subQueryBlock);
1632-
currentBlock.addBody(subQueryBlock);
16331630
final String ret = apply(innerVisitor.visit(headSelectItem, newCtx), context);
16341631
newCtx.leaveScope();
16351632
return ret;
@@ -1640,7 +1637,6 @@ protected String visitGroupByComprehension(final GroupByComprehension node, fina
16401637
final String type = inferType(node.getComprehension().getHead().getSelectExprs().get(0));
16411638
final String listName =
16421639
extractListFromLoop(processedHeadItem, subQueryBlock, newSubqueryName, type);
1643-
currentBlock.addBody(subQueryBlock);
16441640
newCtx.leaveScope();
16451641
// Treat as a vector
16461642
return apply(listName, subQueryBlock, context);
@@ -1740,6 +1736,20 @@ private String createTermsForScalarProduct(final Expr variables, final Expr coef
17401736
extractListFromLoop(coefficientsItem, outerBlock, forLoop, coefficientsType);
17411737
return CodeBlock.of("o.scalProd($L, $L)", listOfVariablesItem, listOfCoefficientsItem).toString();
17421738
}
1739+
1740+
/**
1741+
* Constant sub-queries can be floated to the root block so that we evaluate them only once
1742+
*/
1743+
private void attemptConstantSubqueryOptimization(final MonoidComprehension node,
1744+
final OutputIR.Block subQueryBlock,
1745+
final TranslationContext context) {
1746+
if (IsConstantSubquery.apply(node)) {
1747+
final OutputIR.Block rootBlock = context.getRootBlock();
1748+
rootBlock.addBody(subQueryBlock);
1749+
} else {
1750+
context.currentScope().addBody(subQueryBlock);
1751+
}
1752+
}
17431753
}
17441754

17451755
/**
@@ -1778,7 +1788,7 @@ private String extractListFromLoop(final String variableToExtract, final OutputI
17781788
* @return the name of the list being extracted
17791789
*/
17801790
@SuppressFBWarnings("UPM_UNCALLED_PRIVATE_METHOD") // false positive
1781-
private String extractListFromLoop(final String variableToExtract, final OutputIR.Block outerBlock,
1791+
private String extractListFromLoop(final String variableToExtract, final OutputIR.Block outerBlock,
17821792
final String loopBlockName, final String variableType) {
17831793
final OutputIR.Block forLoop = outerBlock.getForLoopByName(loopBlockName);
17841794
return extractListFromLoop(variableToExtract, outerBlock, forLoop, variableType);
@@ -1865,62 +1875,6 @@ private String getTempViewName() {
18651875
return "tmp" + intermediateViewCounter.getAndIncrement();
18661876
}
18671877

1868-
/**
1869-
* Represents context required for code generation. It maintains a stack of blocks
1870-
* in the IR, that is used to correctly scope variable declarations and accesses.
1871-
*/
1872-
private static class TranslationContext {
1873-
private final Deque<OutputIR.Block> scopeStack;
1874-
private final boolean isFunctionContext;
1875-
1876-
private TranslationContext(final Deque<OutputIR.Block> declarations, final boolean isFunctionContext) {
1877-
this.scopeStack = declarations;
1878-
this.isFunctionContext = isFunctionContext;
1879-
}
1880-
1881-
private TranslationContext(final boolean isFunctionContext) {
1882-
this(new ArrayDeque<>(), isFunctionContext);
1883-
}
1884-
1885-
TranslationContext withEnterFunctionContext() {
1886-
final Deque<OutputIR.Block> stackCopy = new ArrayDeque<>(scopeStack);
1887-
return new TranslationContext(stackCopy, true);
1888-
}
1889-
1890-
boolean isFunctionContext() {
1891-
return isFunctionContext;
1892-
}
1893-
1894-
void enterScope(final OutputIR.Block block) {
1895-
scopeStack.addLast(block);
1896-
}
1897-
1898-
OutputIR.Block currentScope() {
1899-
return Objects.requireNonNull(scopeStack.getLast());
1900-
}
1901-
1902-
OutputIR.Block leaveScope() {
1903-
return scopeStack.removeLast();
1904-
}
1905-
1906-
String declareVariable(final String expression) {
1907-
for (final OutputIR.Block block: scopeStack) {
1908-
if (block.hasDeclaration(expression)) {
1909-
return block.getDeclaredName(expression);
1910-
}
1911-
}
1912-
return scopeStack.getLast().declare(expression);
1913-
}
1914-
1915-
String declareVariable(final String expression, final OutputIR.Block block) {
1916-
return block.declare(expression);
1917-
}
1918-
1919-
String getTupleVarName() {
1920-
return currentScope().getTupleName();
1921-
}
1922-
}
1923-
19241878
private static CodeBlock statement(final String format, final Object... args) {
19251879
return CodeBlock.builder().addStatement(format, args).build();
19261880
}

dcm/src/main/java/com/vmware/dcm/backend/ortools/TranslationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2018-2020 VMware, Inc. All Rights Reserved.
2+
* Copyright 2018-2020 VMware, Inc. All Rights Reserved.
33
* SPDX-License-Identifier: BSD-2
44
*/
55

0 commit comments

Comments
 (0)