diff --git a/query/plugins/org.eclipse.viatra.query.runtime.matchers/src/org/eclipse/viatra/query/runtime/matchers/backend/CommonQueryHintOptions.java b/query/plugins/org.eclipse.viatra.query.runtime.matchers/src/org/eclipse/viatra/query/runtime/matchers/backend/CommonQueryHintOptions.java index 89cfb4b8e6..4219436f56 100644 --- a/query/plugins/org.eclipse.viatra.query.runtime.matchers/src/org/eclipse/viatra/query/runtime/matchers/backend/CommonQueryHintOptions.java +++ b/query/plugins/org.eclipse.viatra.query.runtime.matchers/src/org/eclipse/viatra/query/runtime/matchers/backend/CommonQueryHintOptions.java @@ -8,6 +8,7 @@ *******************************************************************************/ package org.eclipse.viatra.query.runtime.matchers.backend; +import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.ExternalQueryPlanProvider; import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.NopTraceCollector; @@ -28,6 +29,15 @@ private CommonQueryHintOptions() { public static final QueryHintOption normalizationTraceCollector = hintOption("normalizationTraceCollector", NopTraceCollector.INSTANCE); + /** + * This hint allows to plug in an external query planner. + * + * @since 2.8 + */ + public static final QueryHintOption externalQueryPlanProvider = + hintOption("externalQueryPlanProvider", null); + + // internal helper for conciseness private static QueryHintOption hintOption(String hintKeyLocalName, T defaultValue) { return new QueryHintOption<>(CommonQueryHintOptions.class, hintKeyLocalName, defaultValue); diff --git a/query/plugins/org.eclipse.viatra.query.runtime.matchers/src/org/eclipse/viatra/query/runtime/matchers/psystem/rewriters/ExternalQueryPlanProvider.java b/query/plugins/org.eclipse.viatra.query.runtime.matchers/src/org/eclipse/viatra/query/runtime/matchers/psystem/rewriters/ExternalQueryPlanProvider.java new file mode 100644 index 0000000000..f47c916a37 --- /dev/null +++ b/query/plugins/org.eclipse.viatra.query.runtime.matchers/src/org/eclipse/viatra/query/runtime/matchers/psystem/rewriters/ExternalQueryPlanProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.viatra.query.runtime.matchers.psystem.rewriters; + +import org.eclipse.viatra.query.runtime.matchers.backend.CommonQueryHintOptions; +import org.eclipse.viatra.query.runtime.matchers.planning.SubPlan; +import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; + +/** + * An implementation of this interface can provide an execution plan for a {@link PBody}. + * The plan provider can be registered via a query hint (see {@link CommonQueryHintOptions#externalQueryPlanProvider}), + * and it will be used by the Rete recipe compiler. + * + * @since 2.8 + */ +public interface ExternalQueryPlanProvider { + + public SubPlan getPlan(PBody body); + +} diff --git a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java index 24f92933db..2fe0fc9c9f 100644 --- a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java +++ b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java @@ -63,6 +63,7 @@ import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; +import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.ExternalQueryPlanProvider; import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyNormalizer; import org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PDisjunctionRewriter; @@ -132,7 +133,7 @@ public class ReteRecipeCompiler { * @since 2.4 */ protected final TimelyConfiguration timelyEvaluation; - + /** * @since 1.5 */ @@ -140,12 +141,12 @@ public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer) { this(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer, false, null); } - + /** * @since 2.4 */ public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, - IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer, + IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { super(); this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation; @@ -177,7 +178,8 @@ protected boolean shouldExpandWeakenedAlternatives(PQuery query) { private Map queryCompilerCache = new HashMap(); private Set compilationInProgress = new HashSet(); - private IMultiLookup recursionCutoffPoints = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); + private IMultiLookup recursionCutoffPoints = CollectionsFactory + .createMultiLookup(Object.class, MemoryType.SETS, Object.class); private Map subPlanCompilerCache = new HashMap(); private Map compilerBackTrace = new HashMap(); @@ -194,6 +196,7 @@ public void reset() { /** * Returns a {@link CompiledQuery} compiled from a query + * * @throws ViatraQueryRuntimeException */ public CompiledQuery getCompiledForm(PQuery query) { @@ -232,6 +235,7 @@ public CompiledQuery getCompiledForm(PQuery query) { /** * Returns a {@link CompiledSubPlan} compiled from a query plan + * * @throws ViatraQueryRuntimeException */ public CompiledSubPlan getCompiledForm(SubPlan plan) { @@ -273,12 +277,17 @@ public SubPlan getPlan(PBody pBody) { return plan; } - private CompiledQuery compileProduction(PQuery query) { - Collection bodyPlans = new ArrayList(); + private CompiledQuery compileProduction(final PQuery query) { + final Collection bodyPlans = new ArrayList(); + final ExternalQueryPlanProvider externalPlanner = CommonQueryHintOptions.externalQueryPlanProvider + .getValueOrNull(hintProvider.getQueryEvaluationHint(query)); normalizer.setTraceCollector(CommonQueryHintOptions.normalizationTraceCollector .getValueOrDefault(hintProvider.getQueryEvaluationHint(query))); - for (PBody pBody : normalizer.rewrite(query).getBodies()) { - SubPlan bodyPlan = getPlan(pBody); + for (final PBody pBody : normalizer.rewrite(query).getBodies()) { + SubPlan bodyPlan = externalPlanner != null ? externalPlanner.getPlan(pBody) : null; + if (bodyPlan == null) { + bodyPlan = getPlan(pBody); + } bodyPlans.add(bodyPlan); } return doCompileProduction(query, bodyPlans); @@ -301,9 +310,9 @@ private CompiledQuery doCompileProduction(PQuery query, Collection bodi // project to parameter list RecipeTraceInfo finalTrace = projectBodyFinalToParameters(compiledBody, false); - + bodyFinalTraces.put(bodyFinalPlan.getBody(), finalTrace); - bodyFinalRecipes.add(finalTrace.getRecipe()); + bodyFinalRecipes.add(finalTrace.getRecipe()); } CompiledQuery compiled = CompilerHelper.makeQueryTrace(query, bodyFinalTraces, bodyFinalRecipes, @@ -502,7 +511,7 @@ private CompiledSubPlan compileDeferred(PatternMatchCounter constraint, SubPlan final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); final RecipeTraceInfo callProjectionIndexer = fakeJoinHelper.getSecondaryIndexer(); - final List sideVariablesTuple = new ArrayList( + final List sideVariablesTuple = new ArrayList( fakeJoinHelper.getSecondaryMask().transform(callTrace.getVariablesTuple())); /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable()); @@ -579,8 +588,9 @@ private CompiledSubPlan compileDeferred(AggregatorConstraint constraint, SubPlan Mask groupMask = CompilerHelper.toRecipeMask(callGroupMask); // temporary solution to support the deprecated option for now - final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan)); - + final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation + || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan)); + columnAggregatorRecipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { List parameters = constraint.getReferredQuery().getParameters(); @@ -724,48 +734,49 @@ private CompiledSubPlan doCompileProject(PProject operation, SubPlan plan) { List projectedVariables = new ArrayList(operation.getToVariables()); // Determinizing projection: try to keep original order (hopefully facilitates node reuse) Map parentPosMapping = compiledParent.getPosMapping(); - Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get)); - - return doProjectPlan(compiledParent, projectedVariables, true, - parentTrace -> parentTrace.cloneFor(plan), - (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace), - (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace) - ); - } - + Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get)); + + return doProjectPlan(compiledParent, projectedVariables, true, parentTrace -> parentTrace.cloneFor(plan), + (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace), + (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace)); + } + /** * Projects a subplan onto the specified variable tuple - * @param compiledParentPlan the compiled form of the subplan - * @param targetVariables list of variables to project to - * @param enforceUniqueness whether distinctness shall be enforced after the projection. - * Specify false only if directly connecting to a production node. - * @param reinterpretTraceFactory constructs a reinterpreted trace that simply relabels the compiled parent plan, in case it is sufficient - * @param intermediateTraceFactory constructs a recipe trace for an intermediate node, given the recipe of the node and its parent trace - * @param finalTraceFactory constructs a recipe trace for the final resulting node, given the recipe of the node and its parent trace + * + * @param compiledParentPlan + * the compiled form of the subplan + * @param targetVariables + * list of variables to project to + * @param enforceUniqueness + * whether distinctness shall be enforced after the projection. Specify false + * only if directly connecting to a production node. + * @param reinterpretTraceFactory + * constructs a reinterpreted trace that simply relabels the compiled parent + * plan, in case it is sufficient + * @param intermediateTraceFactory + * constructs a recipe trace for an intermediate node, given the recipe of the + * node and its parent trace + * @param finalTraceFactory + * constructs a recipe trace for the final resulting node, given the recipe of + * the node and its parent trace * @since 2.1 */ - ResultTrace doProjectPlan( - final CompiledSubPlan compiledParentPlan, - final List targetVariables, - boolean enforceUniqueness, + ResultTrace doProjectPlan(final CompiledSubPlan compiledParentPlan, + final List targetVariables, boolean enforceUniqueness, Function reinterpretTraceFactory, BiFunction intermediateTraceFactory, - BiFunction finalTraceFactory) - { + BiFunction finalTraceFactory) { if (targetVariables.equals(compiledParentPlan.getVariablesTuple())) // no projection needed - return reinterpretTraceFactory.apply(compiledParentPlan); - + return reinterpretTraceFactory.apply(compiledParentPlan); + // otherwise, we need at least a trimmer TrimmerRecipe trimmerRecipe = CompilerHelper.makeTrimmerRecipe(compiledParentPlan, targetVariables); - + // do we need to eliminate duplicates? SubPlan parentPlan = compiledParentPlan.getSubPlan(); - if (!enforceUniqueness || BuildHelper.areAllVariablesDetermined( - parentPlan, - targetVariables, - queryAnalyzer, - true)) - { + if (!enforceUniqueness + || BuildHelper.areAllVariablesDetermined(parentPlan, targetVariables, queryAnalyzer, true)) { // if uniqueness enforcess is unwanted or unneeeded, skip it return finalTraceFactory.apply(trimmerRecipe, compiledParentPlan); } else { @@ -774,11 +785,13 @@ ResultTrace doProjectPlan( recipe.getParents().add(trimmerRecipe); // temporary solution to support the deprecated option for now - final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan)); - + final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation + || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan)); + recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { - PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, parentPlan.getBody(), metaContext); + PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, parentPlan.getBody(), + metaContext); if (triplet.comparator != null) { MonotonicityInfo info = FACTORY.createMonotonicityInfo(); @@ -788,31 +801,30 @@ ResultTrace doProjectPlan( recipe.setOptionalMonotonicityInfo(info); } } - + RecipeTraceInfo trimmerTrace = intermediateTraceFactory.apply(trimmerRecipe, compiledParentPlan); return finalTraceFactory.apply(recipe, trimmerTrace); } } - + /** * Projects the final compiled form of a PBody onto the parameter tuple - * @param compiledBody the compiled form of the body, with all constraints enforced, not yet projected to query parameters - * @param enforceUniqueness whether distinctness shall be enforced after the projection. - * Specify false only if directly connecting to a production node. + * + * @param compiledBody + * the compiled form of the body, with all constraints enforced, not yet projected to + * query parameters + * @param enforceUniqueness + * whether distinctness shall be enforced after the projection. Specify false only if + * directly connecting to a production node. * @since 2.1 */ - RecipeTraceInfo projectBodyFinalToParameters( - final CompiledSubPlan compiledBody, - boolean enforceUniqueness) - { + RecipeTraceInfo projectBodyFinalToParameters(final CompiledSubPlan compiledBody, boolean enforceUniqueness) { final PBody body = compiledBody.getSubPlan().getBody(); final List parameterList = body.getSymbolicParameterVariables(); - - return doProjectPlan(compiledBody, parameterList, enforceUniqueness, - parentTrace -> parentTrace, + + return doProjectPlan(compiledBody, parameterList, enforceUniqueness, parentTrace -> parentTrace, (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace), - (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace) - ); + (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace)); } private CompiledSubPlan doCompileStart(PStart operation, SubPlan plan) { @@ -858,7 +870,7 @@ private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveC // TODO the implementation would perform better if an inequality check would be used after tcRecipe and // uniqueness enforcer be replaced by a transparent node with multiple parents, but such a node is not available // in recipe metamodel in VIATRA 2.0 - + // Find called query final PQuery referredQuery = constraint.getSupplierKey(); final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); @@ -866,28 +878,32 @@ private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveC // Calculate irreflexive transitive closure final TransitiveClosureRecipe tcRecipe = FACTORY.createTransitiveClosureRecipe(); tcRecipe.setParent(callTrace.getRecipe()); - final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), tcRecipe, callTrace); - + final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), + tcRecipe, callTrace); + // Enumerate universe type final IInputKey inputKey = constraint.getUniverseType(); - final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); + final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), + inputKey.getArity()); final PlanningTrace universeTypeTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple( Tuples.staticArityFlatTupleOf(constraint.getVariablesTuple().get(0))), universeTypeRecipe); - + // Calculate reflexive access by duplicating universe type column final TrimmerRecipe reflexiveRecipe = FACTORY.createTrimmerRecipe(); reflexiveRecipe.setMask(RecipesHelper.mask(1, 0, 0)); reflexiveRecipe.setParent(universeTypeRecipe); - final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), reflexiveRecipe, universeTypeTrace); + final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), + reflexiveRecipe, universeTypeTrace); // Finally, reduce duplicates after a join final UniquenessEnforcerRecipe brtcRecipe = FACTORY.createUniquenessEnforcerRecipe(); brtcRecipe.getParents().add(tcRecipe); brtcRecipe.getParents().add(reflexiveRecipe); - - return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, tcTrace); + + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, + tcTrace); } - + private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure constraint) { final PQuery referredQuery = constraint.getSupplierKey(); final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); @@ -897,7 +913,7 @@ private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure co return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); } - + private PlanningTrace compileEnumerable(SubPlan plan, RelationEvaluation constraint) { final List parentRecipes = new ArrayList(); final List parentTraceInfos = new ArrayList(); @@ -931,7 +947,7 @@ private PlanningTrace compileEnumerable(SubPlan plan, ConstantValue constraint) // TODO handle recursion private PlanningTrace referQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) { - RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query); + RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query); return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(actualParametersTuple), referredQueryTrace.getRecipe(), referredQueryTrace.getParentRecipeTracesForCloning()); } @@ -942,7 +958,7 @@ private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) { Set rewrittenBodies = normalizer.rewrite(query).getBodies(); if (1 == rewrittenBodies.size()) { // non-disjunctive // TODO in the future, check if non-recursive - (not currently permitted) - + PBody pBody = rewrittenBodies.iterator().next(); SubPlan bodyFinalPlan = getPlan(pBody); @@ -956,10 +972,11 @@ private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) { final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); // project to parameter list, add uniqueness enforcer if necessary - return projectBodyFinalToParameters(compiledBody, true /* ensure uniqueness, as no production node is used */); + return projectBodyFinalToParameters(compiledBody, + true /* ensure uniqueness, as no production node is used */); } - } - + } + // otherwise, regular reference to recipe realizing the query return getCompiledForm(query); } diff --git a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/quasitree/JoinCandidate.java b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/quasitree/JoinCandidate.java index 53dad838b3..64feeb0651 100644 --- a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/quasitree/JoinCandidate.java +++ b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/construction/quasitree/JoinCandidate.java @@ -30,7 +30,7 @@ * @author Gabor Bergmann * */ -class JoinCandidate { +public class JoinCandidate { private QueryAnalyzer analyzer; SubPlan primary; @@ -44,7 +44,7 @@ class JoinCandidate { List consSecondary; - JoinCandidate(SubPlan primary, SubPlan secondary, QueryAnalyzer analyzer) { + public JoinCandidate(SubPlan primary, SubPlan secondary, QueryAnalyzer analyzer) { super(); this.primary = primary; this.secondary = secondary; diff --git a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/eval/RelationEvaluatorNode.java b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/eval/RelationEvaluatorNode.java index 95ddc2c7d4..f600ec995d 100644 --- a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/eval/RelationEvaluatorNode.java +++ b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/eval/RelationEvaluatorNode.java @@ -9,11 +9,13 @@ package org.eclipse.viatra.query.runtime.rete.eval; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.stream.IntStream; import org.eclipse.viatra.query.runtime.matchers.psystem.IRelationEvaluator; import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.RelationEvaluation; @@ -28,6 +30,7 @@ import org.eclipse.viatra.query.runtime.rete.network.ReteContainer; import org.eclipse.viatra.query.runtime.rete.network.StandardNode; import org.eclipse.viatra.query.runtime.rete.network.Supplier; +import org.eclipse.viatra.query.runtime.rete.network.communication.CommunicationTracker; import org.eclipse.viatra.query.runtime.rete.network.communication.Timestamp; import org.eclipse.viatra.query.runtime.rete.single.AbstractUniquenessEnforcerNode; @@ -46,12 +49,22 @@ public class RelationEvaluatorNode extends StandardNode implements Supplier, Cle private Supplier[] inputSuppliers; private BatchingReceiver[] inputReceivers; - public RelationEvaluatorNode(final ReteContainer container, final IRelationEvaluator evaluator) { + public RelationEvaluatorNode(final ReteContainer container, final IRelationEvaluator evaluator, + final BatchingReceiver[] inputReceivers) { super(container); this.evaluator = evaluator; + this.inputReceivers = inputReceivers; + for (final BatchingReceiver inputReceiver : inputReceivers) { + inputReceiver.setContainerNode(this); + } this.reteContainer.registerClearable(this); } - + + public RelationEvaluatorNode(final ReteContainer container, final IRelationEvaluator evaluator) { + this(container, evaluator, IntStream.range(0, evaluator.getInputArities().size()) + .mapToObj(i -> new BatchingReceiver(container)).toArray(BatchingReceiver[]::new)); + } + @Override public void clear() { this.cachedOutputs.clear(); @@ -59,7 +72,6 @@ public void clear() { public void connectToParents(final List inputSuppliers) { this.inputSuppliers = new Supplier[inputSuppliers.size()]; - this.inputReceivers = new BatchingReceiver[inputSuppliers.size()]; final List inputArities = evaluator.getInputArities(); @@ -85,10 +97,10 @@ public void connectToParents(final List inputSuppliers) { evaluator.toString() + " expects input arity " + currentExpectedInputArity + " at position " + i + " but got " + currentActualInputArity + "!"); } - final BatchingReceiver inputReceiver = new BatchingReceiver((ProductionNode) inputSupplier, - this.reteContainer); + final BatchingReceiver inputReceiver = this.inputReceivers[i]; + inputReceiver.setSourceNode((ProductionNode) inputSupplier); + this.inputSuppliers[i] = inputSupplier; - this.inputReceivers[i] = inputReceiver; this.reteContainer.connectAndSynchronize(inputSupplier, inputReceiver); reteContainer.getCommunicationTracker().registerDependency(inputReceiver, this); } @@ -153,16 +165,32 @@ private void batchUpdateCompleted() { this.cachedOutputs = newOutputs; } - public class BatchingReceiver extends SimpleReceiver { - private final ProductionNode source; + public static class BatchingReceiver extends SimpleReceiver { + private ProductionNode sourceNode; + private RelationEvaluatorNode containerNode; - private BatchingReceiver(final ProductionNode source, final ReteContainer container) { + private BatchingReceiver(final ReteContainer container) { super(container); - this.source = source; + } + + public void setSourceNode(final ProductionNode sourceNode) { + this.sourceNode = sourceNode; + } + + public ProductionNode getSourceNode() { + return this.sourceNode; + } + + public void setContainerNode(final RelationEvaluatorNode containerNode) { + this.containerNode = containerNode; + } + + public RelationEvaluatorNode getContainerNode() { + return this.containerNode; } private Set getTuples() { - return ((AbstractUniquenessEnforcerNode) this.source).getTuples(); + return ((AbstractUniquenessEnforcerNode) this.sourceNode).getTuples(); } @Override @@ -173,9 +201,25 @@ public void update(final Direction direction, final Tuple updateElement, final T @Override public void batchUpdate(final Collection> updates, final Timestamp timestamp) { assert Timestamp.ZERO.equals(timestamp); - // there is nothing to do here because the source production node has already updated itself - // the only thing we need to do is to issue the callback - RelationEvaluatorNode.this.batchUpdateCompleted(); + // The source production node has already updated itself, so there is no need to do anything with the input + // updates. We will just use the tuples maintained in the memory of the production node. + // However, we should guard against spurious calls to the evaluation logic as much as possible, and only + // really issue the call if this is the "last" batchUpdate call among all the batching receivers of this + // relation evaluator node. It can happen that certain batching receivers are "lacking behind" because + // their ancestor may not have processed their updates yet. In such cases, the batchUpdateCompleted will + // be called potentially unnecessarily again, which is an issue for performance but not an issue for + // correctness. + final CommunicationTracker tracker = this.containerNode.getCommunicationTracker(); + if (Arrays.stream(this.containerNode.inputReceivers).noneMatch(receiver -> { + return tracker.isEnqueued(receiver); + })) { + this.containerNode.batchUpdateCompleted(); + } + } + + @Override + protected String getTraceInfoPatternsEnumerated() { + return this.getContainerNode().toString(); } } diff --git a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationGroup.java b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationGroup.java index a442a3f9d5..1b67dbd3ff 100644 --- a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationGroup.java +++ b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationGroup.java @@ -97,7 +97,7 @@ public boolean equals(final Object obj) { @Override public int compareTo(final CommunicationGroup that) { - return this.identifier - that.identifier; + return Integer.compare(this.identifier, that.identifier); } } diff --git a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationTracker.java b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationTracker.java index 923f7b7309..1282b59abb 100644 --- a/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationTracker.java +++ b/query/plugins/org.eclipse.viatra.query.runtime.rete/src/org/eclipse/viatra/query/runtime/rete/network/communication/CommunicationTracker.java @@ -98,6 +98,13 @@ public CommunicationTracker() { this.groupQueue = new PriorityQueue(); this.groupMap = new HashMap(); } + + /** + * @since 2.8 + */ + public boolean isEnqueued(final Node node) { + return this.groupQueue.contains(this.getGroup(node)); + } public Graph getDependencyGraph() { return dependencyGraph;