/// <summary> /// Parallelize the scheduled search plan for usage from a parallelized matcher /// (non- is-matched-flag-based isomorphy checking) /// </summary> public static void Parallelize(LGSPMatchingPattern matchingPattern) { Debug.Assert(matchingPattern.patternGraph.schedulesIncludingNegativesAndIndependents.Length == 1); ScheduledSearchPlan ssp = matchingPattern.patternGraph.schedulesIncludingNegativesAndIndependents[0]; matchingPattern.patternGraph.parallelizedSchedule = new ScheduledSearchPlan[1]; List <SearchOperation> operations = new List <SearchOperation>(ssp.Operations.Length); for (int i = 0; i < ssp.Operations.Length; ++i) { SearchOperation so = ssp.Operations[i]; SearchOperation clone = (SearchOperation)so.Clone(); clone.Isomorphy.Parallel = true; operations.Add(clone); if (clone.Element is PatternCondition) { SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression); } } ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan( matchingPattern.patternGraph, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0); matchingPattern.patternGraph.parallelizedSchedule[0] = clonedSsp; ParallelizeNegativeIndependent(clonedSsp); ParallelizeAlternativeIterated(matchingPattern.patternGraph); ParallelizeYielding(matchingPattern.patternGraph); }
private static void InsertInlinedIndependentCheckForDuplicateMatch(List <SearchOperation> operations) { bool isInlinedIndependentElementExisting = false; foreach (SearchOperation op in operations) { if (SearchPlanGraphGeneratorAndScheduler.IsOperationAnInlinedIndependentElement(op)) { isInlinedIndependentElementExisting = true; break; } } if (isInlinedIndependentElementExisting) { // insert at end of schedule, just moved ahead over negatives and independents // TODO: if we can estimate condition overhead, it makes sense to move ahead over conditions, too int i = operations.Count - 1; while ((operations[i].Type == SearchOperationType.NegativePattern || operations[i].Type == SearchOperationType.IndependentPattern) && i > 0) { --i; } SearchOperation so = new SearchOperation(SearchOperationType.InlinedIndependentCheckForDuplicateMatch, null, null, operations[i].CostToEnd); operations.Insert(i + 1, so); } }
/// <summary> /// Non- is-matched-flag-based isomorphy checking for nested alternative cases/iterateds /// </summary> private static void ParallelizeAlternativeIterated(PatternGraph patternGraph) { foreach (Alternative alt in patternGraph.alternativesPlusInlined) { foreach (PatternGraph altCase in alt.alternativeCases) { ScheduledSearchPlan ssp = altCase.schedulesIncludingNegativesAndIndependents[0]; altCase.parallelizedSchedule = new ScheduledSearchPlan[1]; List <SearchOperation> operations = new List <SearchOperation>(ssp.Operations.Length); for (int i = 0; i < ssp.Operations.Length; ++i) { SearchOperation so = ssp.Operations[i]; SearchOperation clone = (SearchOperation)so.Clone(); clone.Isomorphy.Parallel = true; operations.Add(clone); if (clone.Element is PatternCondition) { SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression); } } ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan( altCase, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0); altCase.parallelizedSchedule[0] = clonedSsp; ParallelizeNegativeIndependent(clonedSsp); ParallelizeAlternativeIterated(altCase); ParallelizeYielding(altCase); } } foreach (Iterated iter in patternGraph.iteratedsPlusInlined) { ScheduledSearchPlan ssp = iter.iteratedPattern.schedulesIncludingNegativesAndIndependents[0]; iter.iteratedPattern.parallelizedSchedule = new ScheduledSearchPlan[1]; List <SearchOperation> operations = new List <SearchOperation>(ssp.Operations.Length); for (int i = 0; i < ssp.Operations.Length; ++i) { SearchOperation so = ssp.Operations[i]; SearchOperation clone = (SearchOperation)so.Clone(); clone.Isomorphy.Parallel = true; operations.Add(clone); if (clone.Element is PatternCondition) { SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression); } } ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan( iter.iteratedPattern, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0); iter.iteratedPattern.parallelizedSchedule[0] = clonedSsp; ParallelizeNegativeIndependent(clonedSsp); ParallelizeAlternativeIterated(iter.iteratedPattern); ParallelizeYielding(iter.iteratedPattern); } }
public static bool IsOperationAnInlinedIndependentElement(SearchOperation so) { if (so.Element is SearchPlanNode) { if (((SearchPlanNode)so.Element).PatternElement.OriginalIndependentElement != null) { return(true); } } return(false); }
/// <summary> /// Non- is-matched-flag-based isomorphy checking for nested negatives/independents, /// patch into already cloned parallel ssp /// </summary> private static void ParallelizeNegativeIndependent(ScheduledSearchPlan ssp) { foreach (SearchOperation so in ssp.Operations) { so.Isomorphy.Parallel = true; if (so.Type == SearchOperationType.NegativePattern || so.Type == SearchOperationType.IndependentPattern) { ScheduledSearchPlan nestedSsp = (ScheduledSearchPlan)so.Element; List <SearchOperation> operations = new List <SearchOperation>(nestedSsp.Operations.Length); for (int i = 0; i < nestedSsp.Operations.Length; ++i) { SearchOperation nestedSo = nestedSsp.Operations[i]; SearchOperation clone = (SearchOperation)nestedSo.Clone(); operations.Add(clone); if (clone.Element is PatternCondition) { SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression); } } Debug.Assert(nestedSsp.PatternGraph.parallelizedSchedule == null); // may fire in case explain was used before, ignore it then nestedSsp.PatternGraph.parallelizedSchedule = new ScheduledSearchPlan[1]; ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan( nestedSsp.PatternGraph, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0); nestedSsp.PatternGraph.parallelizedSchedule[0] = clonedSsp; so.Element = clonedSsp; ParallelizeNegativeIndependent(clonedSsp); ParallelizeAlternativeIterated(nestedSsp.PatternGraph); if (so.Type == SearchOperationType.IndependentPattern) { ParallelizeYielding(nestedSsp.PatternGraph); } } } }
private static void InsertInlinedElementIdentityCheckIntoSchedule(PatternGraph patternGraph, List <SearchOperation> operations) { for (int i = 0; i < operations.Count; ++i) { PatternElement assignmentSource = null; if (operations[i].Element is SearchPlanNode) { assignmentSource = ((SearchPlanNode)operations[i].Element).PatternElement.AssignmentSource; } if (assignmentSource != null && operations[i].Type != SearchOperationType.Identity) { for (int j = 0; j < operations.Count; ++j) { SearchPlanNode binder = null; if (operations[j].Element is SearchPlanNode) { binder = (SearchPlanNode)operations[j].Element; } if (binder != null && binder.PatternElement == assignmentSource && operations[j].Type != SearchOperationType.Identity) { if (operations[i].Type != SearchOperationType.Assign && operations[j].Type != SearchOperationType.Assign) { int indexOfSecond = Math.Max(i, j); SearchOperation so = new SearchOperation(SearchOperationType.Identity, operations[i].Element, binder, operations[indexOfSecond].CostToEnd); operations.Insert(indexOfSecond + 1, so); break; } } } } } }
public static void Explain(SearchOperation so, SourceBuilder sb, IGraphModel model) { SearchPlanNode src = so.SourceSPNode as SearchPlanNode; SearchPlanNode tgt = so.Element as SearchPlanNode; String connectednessCheck = ""; if (so.ConnectednessCheck.PatternElementName != null) { connectednessCheck = " check " + so.ConnectednessCheck.PatternNodeName + (so.ConnectednessCheck.TheOtherPatternNodeName != null ? " or " + so.ConnectednessCheck.TheOtherPatternNodeName : "") + " connected to " + so.ConnectednessCheck.PatternEdgeName; } switch (so.Type) { case SearchOperationType.Outgoing: sb.AppendFront("from " + src.PatternElement.UnprefixedName + " outgoing -" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "->" + connectednessCheck + "\n"); break; case SearchOperationType.Incoming: sb.AppendFront("from " + src.PatternElement.UnprefixedName + " incoming <-" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "-" + connectednessCheck + "\n"); break; case SearchOperationType.Incident: sb.AppendFront("from " + src.PatternElement.UnprefixedName + " incident <-" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "->" + connectednessCheck + "\n"); break; case SearchOperationType.ImplicitSource: sb.AppendFront("from <-" + src.PatternElement.UnprefixedName + "- get source " + tgt.PatternElement.UnprefixedName + ":" + model.NodeModel.Types[tgt.PatternElement.TypeID].Name + connectednessCheck + "\n"); break; case SearchOperationType.ImplicitTarget: sb.AppendFront("from -" + src.PatternElement.UnprefixedName + "-> get target " + tgt.PatternElement.UnprefixedName + ":" + model.NodeModel.Types[tgt.PatternElement.TypeID].Name + connectednessCheck + "\n"); break; case SearchOperationType.Implicit: sb.AppendFront("from <-" + src.PatternElement.UnprefixedName + "-> get implicit " + tgt.PatternElement.UnprefixedName + ":" + model.NodeModel.Types[tgt.PatternElement.TypeID].Name + connectednessCheck + "\n"); break; case SearchOperationType.Lookup: if (tgt.PatternElement is PatternNode) { sb.AppendFront("lookup " + tgt.PatternElement.UnprefixedName + ":" + model.NodeModel.Types[tgt.PatternElement.TypeID].Name + " in graph" + connectednessCheck + "\n"); } else { sb.AppendFront("lookup -" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "-> in graph" + connectednessCheck + "\n"); } break; case SearchOperationType.ActionPreset: sb.AppendFront("(preset: " + tgt.PatternElement.UnprefixedName + connectednessCheck + ")\n"); break; case SearchOperationType.NegIdptPreset: sb.AppendFront("(preset: " + tgt.PatternElement.UnprefixedName + (tgt.PatternElement.PresetBecauseOfIndependentInlining ? " after independent inlining" : "") + connectednessCheck + ")\n"); break; case SearchOperationType.SubPreset: sb.AppendFront("(preset: " + tgt.PatternElement.UnprefixedName + connectednessCheck + ")\n"); break; case SearchOperationType.Condition: sb.AppendFront("if { depending on " + String.Join(",", ((PatternCondition)so.Element).NeededNodeNames) + (((PatternCondition)so.Element).NeededNodeNames.Length != 0 && ((PatternCondition)so.Element).NeededEdgeNames.Length != 0 ? "," : "") + String.Join(",", ((PatternCondition)so.Element).NeededEdgeNames) + " }\n"); break; case SearchOperationType.NegativePattern: sb.AppendFront("negative {\n"); sb.Indent(); Explain(((ScheduledSearchPlan)so.Element), sb, model); sb.Append("\n"); ((ScheduledSearchPlan)so.Element).PatternGraph.ExplainNested(sb, model); sb.Unindent(); sb.AppendFront("}\n"); break; case SearchOperationType.IndependentPattern: sb.AppendFront("independent {\n"); sb.Indent(); Explain(((ScheduledSearchPlan)so.Element), sb, model); sb.Append("\n"); ((ScheduledSearchPlan)so.Element).PatternGraph.ExplainNested(sb, model); sb.Unindent(); sb.AppendFront("}\n"); break; case SearchOperationType.PickFromStorage: case SearchOperationType.PickFromStorageDependent: sb.AppendFront(tgt.PatternElement.UnprefixedName + "{" + so.Storage.ToString() + "}" + connectednessCheck + "\n"); break; case SearchOperationType.MapWithStorage: case SearchOperationType.MapWithStorageDependent: sb.AppendFront(tgt.PatternElement.UnprefixedName + "{" + so.Storage.ToString() + "[" + so.StorageIndex.ToString() + "]}" + connectednessCheck + "\n"); break; case SearchOperationType.PickFromIndex: case SearchOperationType.PickFromIndexDependent: sb.AppendFront(tgt.PatternElement.UnprefixedName + "{" + so.IndexAccess.ToString() + "}" + connectednessCheck + "\n"); break; case SearchOperationType.PickByName: case SearchOperationType.PickByNameDependent: sb.AppendFront(tgt.PatternElement.UnprefixedName + "{" + so.NameLookup.ToString() + "}" + connectednessCheck + "\n"); break; case SearchOperationType.PickByUnique: case SearchOperationType.PickByUniqueDependent: sb.AppendFront(tgt.PatternElement.UnprefixedName + "{" + so.UniqueLookup.ToString() + "}" + connectednessCheck + "\n"); break; case SearchOperationType.Cast: sb.AppendFront(tgt.PatternElement.UnprefixedName + "<" + src.PatternElement.UnprefixedName + ">" + connectednessCheck + "\n"); break; case SearchOperationType.Assign: sb.AppendFront("(" + tgt.PatternElement.UnprefixedName + " = " + src.PatternElement.UnprefixedName + ")\n"); break; case SearchOperationType.Identity: sb.AppendFront("(" + tgt.PatternElement.UnprefixedName + " == " + src.PatternElement.UnprefixedName + ")\n"); break; case SearchOperationType.AssignVar: sb.AppendFront("(" + tgt.PatternElement.UnprefixedName + " = expr" + ")\n"); break; case SearchOperationType.LockLocalElementsForPatternpath: sb.AppendFront("lock for patternpath\n"); break; case SearchOperationType.DefToBeYieldedTo: if (so.Element is PatternVariable) { sb.AppendFront("def " + ((PatternVariable)so.Element).Name + "\n"); } else { sb.AppendFront("def " + ((SearchPlanNode)so.Element).PatternElement.Name + "\n"); } break; case SearchOperationType.ParallelLookup: if (tgt.PatternElement is PatternNode) { sb.AppendFront("parallelized lookup " + tgt.PatternElement.UnprefixedName + ":" + model.NodeModel.Types[tgt.PatternElement.TypeID].Name + " in graph\n"); } else { sb.AppendFront("parallelized lookup -" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "-> in graph\n"); } break; case SearchOperationType.ParallelPickFromStorage: case SearchOperationType.ParallelPickFromStorageDependent: sb.AppendFront("parallelized " + tgt.PatternElement.UnprefixedName + "{" + so.Storage.ToString() + "}\n"); break; case SearchOperationType.ParallelOutgoing: sb.AppendFront("parallelized from " + src.PatternElement.UnprefixedName + " outgoing -" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "->\n"); break; case SearchOperationType.ParallelIncoming: sb.AppendFront("parallelized from " + src.PatternElement.UnprefixedName + " incoming <-" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "-\n"); break; case SearchOperationType.ParallelIncident: sb.AppendFront("parallelized from " + src.PatternElement.UnprefixedName + " incident <-" + tgt.PatternElement.UnprefixedName + ":" + model.EdgeModel.Types[tgt.PatternElement.TypeID].Name + "->\n"); break; case SearchOperationType.WriteParallelPreset: case SearchOperationType.ParallelPreset: case SearchOperationType.WriteParallelPresetVar: case SearchOperationType.ParallelPresetVar: case SearchOperationType.SetupParallelLookup: case SearchOperationType.SetupParallelPickFromStorage: case SearchOperationType.SetupParallelPickFromStorageDependent: case SearchOperationType.SetupParallelOutgoing: case SearchOperationType.SetupParallelIncoming: case SearchOperationType.SetupParallelIncident: break; // uninteresting to the user } }
/// <summary> /// Generates a scheduled search plan for a given search plan graph /// </summary> public static ScheduledSearchPlan ScheduleSearchPlan(SearchPlanGraph spGraph, PatternGraph patternGraph, bool isNegativeOrIndependent, bool lazyNegativeIndependentConditionEvaluation) { // the schedule List <SearchOperation> operations = new List <SearchOperation>(); // a set of search plan edges representing the currently reachable not yet visited elements PriorityQueue <SearchPlanEdge> activeEdges = new PriorityQueue <SearchPlanEdge>(); // first schedule all preset elements foreach (SearchPlanEdge edge in spGraph.Root.OutgoingEdges) { if (edge.Target.IsPreset && edge.Type != SearchOperationType.DefToBeYieldedTo) { foreach (SearchPlanEdge edgeOutgoingFromPresetElement in edge.Target.OutgoingEdges) { activeEdges.Add(edgeOutgoingFromPresetElement); } // note: here a normal preset is converted into a neg/idpt preset operation if in negative/independent pattern SearchOperation newOp = new SearchOperation( isNegativeOrIndependent ? SearchOperationType.NegIdptPreset : edge.Type, edge.Target, spGraph.Root, 0); operations.Add(newOp); } } // then schedule all map with storage / pick from index / pick from storage / pick from name index elements not depending on other elements foreach (SearchPlanEdge edge in spGraph.Root.OutgoingEdges) { if (edge.Type == SearchOperationType.MapWithStorage) { foreach (SearchPlanEdge edgeOutgoingFromPickedElement in edge.Target.OutgoingEdges) { activeEdges.Add(edgeOutgoingFromPickedElement); } SearchOperation newOp = new SearchOperation(edge.Type, edge.Target, spGraph.Root, 0); newOp.Storage = edge.Target.PatternElement.Storage; newOp.StorageIndex = edge.Target.PatternElement.StorageIndex; operations.Add(newOp); } } foreach (SearchPlanEdge edge in spGraph.Root.OutgoingEdges) { if (edge.Type == SearchOperationType.PickFromStorage) { foreach (SearchPlanEdge edgeOutgoingFromPickedElement in edge.Target.OutgoingEdges) { activeEdges.Add(edgeOutgoingFromPickedElement); } SearchOperation newOp = new SearchOperation(edge.Type, edge.Target, spGraph.Root, 0); newOp.Storage = edge.Target.PatternElement.Storage; operations.Add(newOp); } } foreach (SearchPlanEdge edge in spGraph.Root.OutgoingEdges) { if (edge.Type == SearchOperationType.PickFromIndex) { foreach (SearchPlanEdge edgeOutgoingFromPickedElement in edge.Target.OutgoingEdges) { activeEdges.Add(edgeOutgoingFromPickedElement); } SearchOperation newOp = new SearchOperation(edge.Type, edge.Target, spGraph.Root, 0); newOp.IndexAccess = edge.Target.PatternElement.IndexAccess; operations.Add(newOp); } } foreach (SearchPlanEdge edge in spGraph.Root.OutgoingEdges) { if (edge.Type == SearchOperationType.PickByName) { foreach (SearchPlanEdge edgeOutgoingFromPickedElement in edge.Target.OutgoingEdges) { activeEdges.Add(edgeOutgoingFromPickedElement); } SearchOperation newOp = new SearchOperation(edge.Type, edge.Target, spGraph.Root, 0); newOp.NameLookup = edge.Target.PatternElement.NameLookup; operations.Add(newOp); } } foreach (SearchPlanEdge edge in spGraph.Root.OutgoingEdges) { if (edge.Type == SearchOperationType.PickByUnique) { foreach (SearchPlanEdge edgeOutgoingFromPickedElement in edge.Target.OutgoingEdges) { activeEdges.Add(edgeOutgoingFromPickedElement); } SearchOperation newOp = new SearchOperation(edge.Type, edge.Target, spGraph.Root, 0); newOp.UniqueLookup = edge.Target.PatternElement.UniqueLookup; operations.Add(newOp); } } // iterate over all reachable elements until the whole graph has been scheduled(/visited), // choose next cheapest operation, update the reachable elements and the search plan costs SearchPlanNode lastNode = spGraph.Root; for (int i = 0; i < spGraph.Nodes.Length - spGraph.NumPresetElements - spGraph.NumIndependentStorageIndexElements; ++i) { foreach (SearchPlanEdge edge in lastNode.OutgoingEdges) { if (edge.Target.IsPreset) { continue; } if (edge.Target.PatternElement.Storage != null && edge.Target.PatternElement.GetPatternElementThisElementDependsOnOutsideOfGraphConnectedness() == null) { continue; } if (edge.Target.PatternElement.IndexAccess != null && edge.Target.PatternElement.GetPatternElementThisElementDependsOnOutsideOfGraphConnectedness() == null) { continue; } if (edge.Target.PatternElement.NameLookup != null && edge.Target.PatternElement.GetPatternElementThisElementDependsOnOutsideOfGraphConnectedness() == null) { continue; } if (edge.Target.PatternElement.UniqueLookup != null && edge.Target.PatternElement.GetPatternElementThisElementDependsOnOutsideOfGraphConnectedness() == null) { continue; } CostDecreaseForLeavingInlinedIndependent(edge); activeEdges.Add(edge); } SearchPlanEdge minEdge = activeEdges.DequeueFirst(); lastNode = minEdge.Target; SearchOperation newOp = new SearchOperation(minEdge.Type, lastNode, minEdge.Source, minEdge.Cost); newOp.Storage = minEdge.Target.PatternElement.Storage; newOp.StorageIndex = minEdge.Target.PatternElement.StorageIndex; newOp.IndexAccess = minEdge.Target.PatternElement.IndexAccess; newOp.NameLookup = minEdge.Target.PatternElement.NameLookup; newOp.UniqueLookup = minEdge.Target.PatternElement.UniqueLookup; foreach (SearchOperation op in operations) { op.CostToEnd += minEdge.Cost; } operations.Add(newOp); } // remove the elements stemming from inlined independents // they were added in the hope that they might help in matching this pattern, // they don't if they are matched after the elements of this pattern (in fact they only increase the costs then) RemoveInlinedIndependentElementsAtEnd(operations); // insert inlined element identity check into the schedule in case neither of the possible assignments was scheduled InsertInlinedElementIdentityCheckIntoSchedule(patternGraph, operations); // insert inlined variable assignments into the schedule InsertInlinedVariableAssignmentsIntoSchedule(patternGraph, operations); // insert conditions into the schedule InsertConditionsIntoSchedule(patternGraph.ConditionsPlusInlined, operations, lazyNegativeIndependentConditionEvaluation); // schedule the initialization of all def to be yielded to elements and variables at the end, // must come after the pattern elements (and preset elements), as they may be used in the def initialization foreach (SearchPlanEdge edge in spGraph.Root.OutgoingEdges) { if (edge.Type == SearchOperationType.DefToBeYieldedTo && (!isNegativeOrIndependent || (edge.Target.PatternElement.pointOfDefinition == patternGraph && edge.Target.PatternElement.originalElement == null))) { SearchOperation newOp = new SearchOperation( SearchOperationType.DefToBeYieldedTo, edge.Target, spGraph.Root, 0); newOp.Expression = edge.Target.PatternElement.Initialization; operations.Add(newOp); } } foreach (PatternVariable var in patternGraph.variablesPlusInlined) { if (var.defToBeYieldedTo && (!isNegativeOrIndependent || var.pointOfDefinition == patternGraph && var.originalVariable == null)) { SearchOperation newOp = new SearchOperation( SearchOperationType.DefToBeYieldedTo, var, spGraph.Root, 0); newOp.Expression = var.initialization; operations.Add(newOp); } } float cost = operations.Count > 0 ? operations[0].CostToEnd : 0; return(new ScheduledSearchPlan(patternGraph, operations.ToArray(), cost)); }
/// <summary> /// Inserts inlined variable assignments into the schedule given by the operations list at their earliest possible position /// </summary> private static void InsertInlinedVariableAssignmentsIntoSchedule(PatternGraph patternGraph, List <SearchOperation> operations) { // compute the number of inlined parameter variables int numInlinedParameterVariables = 0; foreach (PatternVariable var in patternGraph.variablesPlusInlined) { if (var.AssignmentSource != null && patternGraph.WasInlinedHere(var.originalSubpatternEmbedding)) { ++numInlinedParameterVariables; } } if (numInlinedParameterVariables == 0) { return; } // get the inlined parameter variables and the elements needed in order to compute their defining expression Dictionary <String, PatternElement>[] neededElements = new Dictionary <String, PatternElement> [numInlinedParameterVariables]; PatternVariable[] inlinedParameterVariables = new PatternVariable[numInlinedParameterVariables]; int curInlParamVar = 0; foreach (PatternVariable var in patternGraph.variablesPlusInlined) { if (var.AssignmentSource == null) { continue; } if (!patternGraph.WasInlinedHere(var.originalSubpatternEmbedding)) { continue; } neededElements[curInlParamVar] = new Dictionary <string, PatternElement>(); for (int i = 0; i < var.AssignmentDependencies.neededNodeNames.Length; ++i) { String neededNodeName = var.AssignmentDependencies.neededNodeNames[i]; PatternNode neededNode = var.AssignmentDependencies.neededNodes[i]; neededElements[curInlParamVar][neededNodeName] = neededNode; } for (int i = 0; i < var.AssignmentDependencies.neededEdgeNames.Length; ++i) { String neededEdgeName = var.AssignmentDependencies.neededEdgeNames[i]; PatternEdge neededEdge = var.AssignmentDependencies.neededEdges[i]; neededElements[curInlParamVar][neededEdgeName] = neededEdge; } inlinedParameterVariables[curInlParamVar] = var; ++curInlParamVar; } // iterate over all inlined parameter variables for (int i = 0; i < inlinedParameterVariables.Length; ++i) { int j; float costToEnd = 0; // find leftmost place in scheduled search plan for current assignment // by search from end of schedule forward until the first element the expression assigned is dependent on is found for (j = operations.Count - 1; j >= 0; --j) { SearchOperation op = operations[j]; if (op.Type == SearchOperationType.Condition || op.Type == SearchOperationType.Assign || op.Type == SearchOperationType.AssignVar || op.Type == SearchOperationType.NegativePattern || op.Type == SearchOperationType.IndependentPattern || op.Type == SearchOperationType.DefToBeYieldedTo) { continue; } if (neededElements[i].ContainsKey(((SearchPlanNode)op.Element).PatternElement.Name)) { costToEnd = op.CostToEnd; break; } } SearchOperation so = new SearchOperation(SearchOperationType.AssignVar, inlinedParameterVariables[i], null, costToEnd); so.Expression = inlinedParameterVariables[i].AssignmentSource; operations.Insert(j + 1, so); } }
/// <summary> /// Inserts conditions into the schedule given by the operations list at their earliest possible position /// todo: set/map operations are potentially expensive, they shouldn't be insertes asap, but depending an weight, /// derived from statistics over set/map size for graph elements, quiet well known for anonymous rule sets /// </summary> private static void InsertConditionsIntoSchedule(PatternCondition[] conditions, List <SearchOperation> operations, bool lazyNegativeIndependentConditionEvaluation) { // get needed (in order to evaluate it) elements of each condition Dictionary <String, object>[] neededElements = new Dictionary <String, object> [conditions.Length]; for (int i = 0; i < conditions.Length; ++i) { neededElements[i] = new Dictionary <string, object>(); for (int j = 0; j < conditions[i].NeededNodeNames.Length; ++j) { String neededNodeName = conditions[i].NeededNodeNames[j]; PatternNode neededNode = conditions[i].NeededNodes[j]; neededElements[i][neededNodeName] = neededNode; } for (int j = 0; j < conditions[i].NeededEdgeNames.Length; ++j) { String neededEdgeName = conditions[i].NeededEdgeNames[j]; PatternEdge neededEdge = conditions[i].NeededEdges[j]; neededElements[i][neededEdgeName] = neededEdge; } for (int j = 0; j < conditions[i].NeededVariableNames.Length; ++j) { String neededVariableName = conditions[i].NeededVariableNames[j]; PatternVariable neededVariable = conditions[i].NeededVariables[j]; neededElements[i][neededVariableName] = neededVariable; } } // iterate over all conditions for (int i = 0; i < conditions.Length; ++i) { int j; float costToEnd = 0; // find leftmost place in scheduled search plan for current condition // by search from end of schedule forward until the first element the condition is dependent on is found for (j = operations.Count - 1; j >= 0; --j) { SearchOperation op = operations[j]; if (op.Type == SearchOperationType.Condition || op.Type == SearchOperationType.NegativePattern || op.Type == SearchOperationType.IndependentPattern || op.Type == SearchOperationType.DefToBeYieldedTo) { continue; } if (lazyNegativeIndependentConditionEvaluation) { break; } if (op.Type == SearchOperationType.AssignVar) { if (neededElements[i].ContainsKey(((PatternVariable)op.Element).Name)) { costToEnd = op.CostToEnd; break; } continue; } if (neededElements[i].ContainsKey(((SearchPlanNode)op.Element).PatternElement.Name)) { costToEnd = op.CostToEnd; break; } } operations.Insert(j + 1, new SearchOperation(SearchOperationType.Condition, conditions[i], null, costToEnd)); } }
/// <summary> /// Recursively assembles interpretation plan from scheduled search plan, beginning at index 0. /// Decides which specialized build procedure is to be called. /// The specialized build procedure then calls this procedure again, /// in order to process the next search plan operation at the following index. /// The insertionPoint is the lastly built operation; /// the next operation built is inserted into the next link of it, /// then it becomes the current insertionPoint. /// </summary> private void BuildInterpretationPlan(InterpretationPlan insertionPoint, int index) { if (index >= ssp.Operations.Length) { // end of scheduled search plan reached, stop recursive iteration buildMatchComplete(insertionPoint); return; } SearchOperation op = ssp.Operations[index]; // for current scheduled search plan operation // insert corresponding interpretation plan operations into interpretation plan switch (op.Type) { case SearchOperationType.Lookup: if (((SearchPlanNode)op.Element).NodeType == PlanNodeType.Node) { buildLookup(insertionPoint, index, (SearchPlanNodeNode)op.Element); } else { buildLookup(insertionPoint, index, (SearchPlanEdgeNode)op.Element); } break; case SearchOperationType.ImplicitSource: buildImplicit(insertionPoint, index, (SearchPlanEdgeNode)op.SourceSPNode, (SearchPlanNodeNode)op.Element, ImplicitNodeType.Source); break; case SearchOperationType.ImplicitTarget: buildImplicit(insertionPoint, index, (SearchPlanEdgeNode)op.SourceSPNode, (SearchPlanNodeNode)op.Element, ImplicitNodeType.Target); break; case SearchOperationType.Implicit: buildImplicit(insertionPoint, index, (SearchPlanEdgeNode)op.SourceSPNode, (SearchPlanNodeNode)op.Element, ImplicitNodeType.SourceOrTarget); break; case SearchOperationType.Incoming: buildIncident(insertionPoint, index, (SearchPlanNodeNode)op.SourceSPNode, (SearchPlanEdgeNode)op.Element, IncidentEdgeType.Incoming); break; case SearchOperationType.Outgoing: buildIncident(insertionPoint, index, (SearchPlanNodeNode)op.SourceSPNode, (SearchPlanEdgeNode)op.Element, IncidentEdgeType.Outgoing); break; case SearchOperationType.Incident: buildIncident(insertionPoint, index, (SearchPlanNodeNode)op.SourceSPNode, (SearchPlanEdgeNode)op.Element, IncidentEdgeType.IncomingOrOutgoing); break; case SearchOperationType.Condition: buildCondition(insertionPoint, index, (PatternCondition)op.Element); break; default: throw new Exception("Unknown or unsupported search operation"); } }
/// <summary> /// Parallelize the scheduled search plan to the branching factor, /// splitting it at the first loop into a header part and a body part /// </summary> public static void ParallelizeHeadBody(LGSPRulePattern rulePattern) { Debug.Assert(rulePattern.patternGraph.schedulesIncludingNegativesAndIndependents.Length == 1); ScheduledSearchPlan ssp = rulePattern.patternGraph.schedulesIncludingNegativesAndIndependents[0]; int indexToSplitAt = 0; for (int i = 0; i < ssp.Operations.Length; ++i) { SearchOperation so = ssp.Operations[i]; if (so.Type == SearchOperationType.Lookup || so.Type == SearchOperationType.Incident || so.Type == SearchOperationType.Incoming || so.Type == SearchOperationType.Outgoing || so.Type == SearchOperationType.PickFromStorage || so.Type == SearchOperationType.PickFromStorageDependent || so.Type == SearchOperationType.PickFromIndex || so.Type == SearchOperationType.PickFromIndexDependent) { indexToSplitAt = i; break; } } rulePattern.patternGraph.parallelizedSchedule = new ScheduledSearchPlan[2]; List <SearchOperation> headOperations = new List <SearchOperation>(); List <SearchOperation> bodyOperations = new List <SearchOperation>(); for (int i = 0; i < rulePattern.Inputs.Length; ++i) { if (rulePattern.Inputs[i] is VarType) // those don't appear in the schedule, they are only extracted into the search program { VarType varType = (VarType)rulePattern.Inputs[i]; String varName = rulePattern.InputNames[i]; PatternVariable dummy = new PatternVariable(varType, varName, varName, i, false, null); headOperations.Add(new SearchOperation(SearchOperationType.WriteParallelPresetVar, dummy, null, 0)); bodyOperations.Add(new SearchOperation(SearchOperationType.ParallelPresetVar, dummy, null, 0)); } } for (int i = 0; i < ssp.Operations.Length; ++i) { SearchOperation so = ssp.Operations[i]; if (i < indexToSplitAt) { SearchOperation clone = (SearchOperation)so.Clone(); clone.Isomorphy.Parallel = true; clone.Isomorphy.LockForAllThreads = true; headOperations.Add(clone); switch (so.Type) { // the target binding looping operations can't appear in the header, so we don't treat them here // the non-target binding operations are completely handled by just adding them, happended already above // the target binding non-looping operations are handled below, // by parallel preset writing in the header and reading in the body // with exception of def, its declaration and initializion is just re-executed in the body // some presets can't appear in an action header, they are thus not taken care of case SearchOperationType.ActionPreset: case SearchOperationType.MapWithStorage: case SearchOperationType.MapWithStorageDependent: case SearchOperationType.Cast: case SearchOperationType.Assign: case SearchOperationType.Identity: case SearchOperationType.ImplicitSource: case SearchOperationType.ImplicitTarget: case SearchOperationType.Implicit: headOperations.Add(new SearchOperation(SearchOperationType.WriteParallelPreset, (SearchPlanNode)so.Element, so.SourceSPNode, 0)); bodyOperations.Add(new SearchOperation(SearchOperationType.ParallelPreset, (SearchPlanNode)so.Element, so.SourceSPNode, 0)); break; case SearchOperationType.AssignVar: headOperations.Add(new SearchOperation(SearchOperationType.WriteParallelPresetVar, (PatternVariable)so.Element, so.SourceSPNode, 0)); bodyOperations.Add(new SearchOperation(SearchOperationType.ParallelPresetVar, (PatternVariable)so.Element, so.SourceSPNode, 0)); break; case SearchOperationType.DefToBeYieldedTo: bodyOperations.Add((SearchOperation)so.Clone()); break; } } else if (i == indexToSplitAt) { SearchOperation cloneHead; SearchOperation cloneBody; switch (so.Type) { case SearchOperationType.Lookup: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelLookup); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelLookup); break; case SearchOperationType.Incident: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelIncident); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelIncident); break; case SearchOperationType.Incoming: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelIncoming); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelIncoming); break; case SearchOperationType.Outgoing: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelOutgoing); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelOutgoing); break; case SearchOperationType.PickFromStorage: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelPickFromStorage); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelPickFromStorage); break; case SearchOperationType.PickFromStorageDependent: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelPickFromStorageDependent); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelPickFromStorageDependent); break; case SearchOperationType.PickFromIndex: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelPickFromIndex); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelPickFromIndex); break; case SearchOperationType.PickFromIndexDependent: cloneHead = (SearchOperation)so.Clone(SearchOperationType.SetupParallelPickFromIndexDependent); cloneBody = (SearchOperation)so.Clone(SearchOperationType.ParallelPickFromIndexDependent); break; default: // failure, operation at this index cannot be parallelized/parallelization not supported cloneHead = null; cloneBody = null; break; } headOperations.Add(cloneHead); cloneBody.Isomorphy.Parallel = true; bodyOperations.Add(cloneBody); } else { SearchOperation clone = (SearchOperation)so.Clone(); clone.Isomorphy.Parallel = true; bodyOperations.Add(clone); if (clone.Element is PatternCondition) { SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression); } } } ScheduledSearchPlan headSsp = new ScheduledSearchPlan( rulePattern.patternGraph, headOperations.ToArray(), headOperations.Count > 0 ? headOperations[0].CostToEnd : 0); rulePattern.patternGraph.parallelizedSchedule[0] = headSsp; ScheduledSearchPlan bodySsp = new ScheduledSearchPlan( rulePattern.patternGraph, bodyOperations.ToArray(), bodyOperations.Count > 0 ? bodyOperations[0].CostToEnd : 0); rulePattern.patternGraph.parallelizedSchedule[1] = bodySsp; ParallelizeNegativeIndependent(bodySsp); ParallelizeAlternativeIterated(rulePattern.patternGraph); ParallelizeYielding(rulePattern.patternGraph); }
/// <summary> /// Inserts schedules of negative and independent pattern graphs into the schedule of the enclosing pattern graph /// for the schedule with the given array index /// </summary> private static void InsertNegativesAndIndependentsIntoSchedule(PatternGraph patternGraph, int index, bool lazyNegativeIndependentConditionEvaluation) { // todo: erst implicit node, dann negative/independent, auch wenn negative/independent mit erstem implicit moeglich wird patternGraph.schedulesIncludingNegativesAndIndependents[index] = null; // an explain might have filled this List <SearchOperation> operations = new List <SearchOperation>(); for (int i = 0; i < patternGraph.schedules[index].Operations.Length; ++i) { operations.Add(patternGraph.schedules[index].Operations[i]); } // nested patterns on the way to an enclosed patternpath modifier // must get matched after all local nodes and edges, because they require // all outer elements to be known in order to lock them for patternpath processing if (patternGraph.patternGraphsOnPathToEnclosedPatternpath .Contains(patternGraph.pathPrefix + patternGraph.name)) { operations.Add(new SearchOperation(SearchOperationType.LockLocalElementsForPatternpath, null, null, patternGraph.schedules[index].Operations.Length != 0 ? patternGraph.schedules[index].Operations[patternGraph.schedules[index].Operations.Length - 1].CostToEnd : 0)); } // iterate over all negative scheduled search plans (TODO: order?) for (int i = 0; i < patternGraph.negativePatternGraphsPlusInlined.Length; ++i) { ScheduledSearchPlan negSchedule = patternGraph.negativePatternGraphsPlusInlined[i].schedulesIncludingNegativesAndIndependents[0]; int bestFitIndex = operations.Count; float bestFitCostToEnd = 0; // find best place in scheduled search plan for current negative pattern // during search from end of schedule forward until the first element the negative pattern is dependent on is found for (int j = operations.Count - 1; j >= 0; --j) { SearchOperation op = operations[j]; if (op.Type == SearchOperationType.Condition || op.Type == SearchOperationType.NegativePattern || op.Type == SearchOperationType.IndependentPattern || op.Type == SearchOperationType.AssignVar) { continue; } if (lazyNegativeIndependentConditionEvaluation) { break; } if (op.Type == SearchOperationType.LockLocalElementsForPatternpath || op.Type == SearchOperationType.DefToBeYieldedTo) { break; // LockLocalElementsForPatternpath and DefToBeYieldedTo are barriers for neg/idpt } if (patternGraph.negativePatternGraphsPlusInlined[i].neededNodes.ContainsKey(((SearchPlanNode)op.Element).PatternElement.Name) || patternGraph.negativePatternGraphsPlusInlined[i].neededEdges.ContainsKey(((SearchPlanNode)op.Element).PatternElement.Name)) { break; } if (negSchedule.Cost <= op.CostToEnd) { // best fit as CostToEnd is monotonously growing towards operation[0] bestFitIndex = j; bestFitCostToEnd = op.CostToEnd; } } // insert pattern at best position operations.Insert(bestFitIndex, new SearchOperation(SearchOperationType.NegativePattern, negSchedule, null, bestFitCostToEnd + negSchedule.Cost)); // update costs of operations before best position for (int j = 0; j < bestFitIndex; ++j) { operations[j].CostToEnd += negSchedule.Cost; } } // iterate over all independent scheduled search plans (TODO: order?) for (int i = 0; i < patternGraph.independentPatternGraphsPlusInlined.Length; ++i) { ScheduledSearchPlan idptSchedule = patternGraph.independentPatternGraphsPlusInlined[i].schedulesIncludingNegativesAndIndependents[0]; int bestFitIndex = operations.Count; float bestFitCostToEnd = 0; IDictionary <PatternElement, SetValueType> presetsFromIndependentInlining = ExtractOwnElements(patternGraph.schedules[index], patternGraph.independentPatternGraphsPlusInlined[i]); // find best place in scheduled search plan for current independent pattern // during search from end of schedule forward until the first element the independent pattern is dependent on is found for (int j = operations.Count - 1; j >= 0; --j) { SearchOperation op = operations[j]; if (op.Type == SearchOperationType.Condition || op.Type == SearchOperationType.NegativePattern || op.Type == SearchOperationType.IndependentPattern || op.Type == SearchOperationType.AssignVar) { continue; } if (lazyNegativeIndependentConditionEvaluation) { break; } if (op.Type == SearchOperationType.LockLocalElementsForPatternpath || op.Type == SearchOperationType.DefToBeYieldedTo) { break; // LockLocalElementsForPatternpath and DefToBeYieldedTo are barriers for neg/idpt } PatternElement pe = ((SearchPlanNode)op.Element).PatternElement; if (patternGraph.independentPatternGraphsPlusInlined[i].neededNodes.ContainsKey(pe.Name) || patternGraph.independentPatternGraphsPlusInlined[i].neededEdges.ContainsKey(pe.Name)) { break; } if (pe.OriginalIndependentElement != null && presetsFromIndependentInlining.ContainsKey(pe.OriginalIndependentElement)) { break; } if (idptSchedule.Cost <= op.CostToEnd) { // best fit as CostToEnd is monotonously growing towards operation[0] bestFitIndex = j; bestFitCostToEnd = op.CostToEnd; } } // insert pattern at best position operations.Insert(bestFitIndex, new SearchOperation(SearchOperationType.IndependentPattern, idptSchedule, null, bestFitCostToEnd + idptSchedule.Cost)); // update costs of operations before best position for (int j = 0; j < bestFitIndex; ++j) { operations[j].CostToEnd += idptSchedule.Cost; } } InsertInlinedIndependentCheckForDuplicateMatch(operations); float cost = operations.Count > 0 ? operations[0].CostToEnd : 0; patternGraph.schedulesIncludingNegativesAndIndependents[index] = new ScheduledSearchPlan(patternGraph, operations.ToArray(), cost); }