From 19410c7f0068ac7958ce0f97de303c09103ac37d Mon Sep 17 00:00:00 2001 From: Shaokai Jerry Lin Date: Tue, 12 Nov 2024 17:39:02 -0800 Subject: [PATCH] Track instructions (transition guards) that contribute to hyperperiod startup overhead --- .../java/org/lflang/analyses/dag/Dag.java | 25 +++++--- .../java/org/lflang/analyses/dag/DagNode.java | 2 +- .../analyses/pretvm/InstructionGenerator.java | 62 ++++++++++++++----- .../pretvm/instructions/Instruction.java | 16 +++-- 4 files changed, 77 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/lflang/analyses/dag/Dag.java b/core/src/main/java/org/lflang/analyses/dag/Dag.java index 0f39e6cc05..c561c4d7c9 100644 --- a/core/src/main/java/org/lflang/analyses/dag/Dag.java +++ b/core/src/main/java/org/lflang/analyses/dag/Dag.java @@ -392,13 +392,24 @@ public CodeBuilder generateDot(List> instructions) { } // Add PretVM instructions. - if (instructions != null && node.nodeType == DagNode.dagNodeType.REACTION) { - int worker = node.getWorker(); - List workerInstructions = instructions.get(worker); - if (node.filterInstructions(workerInstructions).size() > 0) - label += "\\n" + "Instructions:"; - for (Instruction inst : node.filterInstructions(workerInstructions)) { - label += "\\n" + inst.getOpcode() + " (worker " + inst.getWorker() + ")"; + if (instructions != null) { + if (node.nodeType == DagNode.dagNodeType.REACTION) { + int worker = node.getWorker(); + List workerInstructions = instructions.get(worker); + if (node.filterInstructions(workerInstructions).size() > 0) + label += "\\n" + "Instructions:"; + for (Instruction inst : node.filterInstructions(workerInstructions)) { + label += "\\n" + inst.getOpcode(); + } + } + else if (node.nodeType == DagNode.dagNodeType.SYNC) { + int workers = instructions.size(); + for (int worker = 0; worker < workers; worker++) { + List workerInstructions = instructions.get(worker); + for (Instruction inst : node.filterInstructions(workerInstructions)) { + label += "\\n" + inst.getOpcode() + " (worker " + inst.getWorker() + ")"; + } + } } } diff --git a/core/src/main/java/org/lflang/analyses/dag/DagNode.java b/core/src/main/java/org/lflang/analyses/dag/DagNode.java index 8bfcb99979..08b4bd0a18 100644 --- a/core/src/main/java/org/lflang/analyses/dag/DagNode.java +++ b/core/src/main/java/org/lflang/analyses/dag/DagNode.java @@ -153,7 +153,7 @@ public void setReleaseValue(Long value) { * collect instructions which belong to that node. */ public List filterInstructions(List workerInstructions) { - return workerInstructions.stream().filter(it -> it.getDagNode() == this).toList(); + return workerInstructions.stream().filter(it -> it.getDagNodes().contains(this)).toList(); } /** diff --git a/core/src/main/java/org/lflang/analyses/pretvm/InstructionGenerator.java b/core/src/main/java/org/lflang/analyses/pretvm/InstructionGenerator.java index 46553861a3..c3f64f597b 100644 --- a/core/src/main/java/org/lflang/analyses/pretvm/InstructionGenerator.java +++ b/core/src/main/java/org/lflang/analyses/pretvm/InstructionGenerator.java @@ -1770,9 +1770,15 @@ private String getWorkerLabelString(Object label, int worker) { } /** - * Link multiple object files into a single executable (represented also in an object file class). + * Link multiple object files into a single executable (represented also in an object file class). * Instructions are also inserted based on transition guards between fragments. In addition, * PREAMBLE and EPILOGUE instructions are inserted here. + * + * Very importantly, transition guards are added to the DAG start + * nodes of the downstream fragments, because they are placed after + * the sync block and DU, so they should factor into the startup + * overhead of the next hyperperiod. Locations marked by "STARTUP + * OVERHEAD REASONING" is related to this. */ public PretVmExecutable link(List pretvmObjectFiles, Path graphDir) { @@ -1814,31 +1820,55 @@ public PretVmExecutable link(List pretvmObjectFiles, Path grap // Obtain partial schedules from the current object file. List> partialSchedules = current.getContent(); - // Append guards for downstream transitions to the partial schedules. + // Declare placeholders for default transition and default + // fragment. They need to be added last, after the other + // transitions. List defaultTransition = null; - for (var dsFragment : downstreamFragments) { - List transition = current.getFragment().getDownstreams().get(dsFragment); + StateSpaceFragment defaultDownstreamFragment = null; + // Append guards for downstream transitions to the partial schedules. + for (StateSpaceFragment dsFragment : downstreamFragments) { + List abstractTransition = current.getFragment().getDownstreams().get(dsFragment); // Check if a transition is a default transition. - if (StateSpaceUtils.isDefaultTransition(transition)) { - defaultTransition = transition; + // If so, save them for later. + if (StateSpaceUtils.isDefaultTransition(abstractTransition)) { + defaultTransition = abstractTransition; + defaultDownstreamFragment = dsFragment; continue; } // Add COPIES of guarded transitions to the partial schedules. // They have to be copies since otherwise labels created for different - // workers will be added to the same instruction object, creating conflicts. - for (int i = 0; i < workers; i++) { - partialSchedules - .get(i) - .addAll(replaceAbstractRegistersToConcreteRegisters(transition, i)); + // workers will be added to the same instruction object, + // creating conflicts. + for (int w = 0; w < workers; w++) { + // Replace the abstract registers with concrete registers. + List concreteTransition = replaceAbstractRegistersToConcreteRegisters(abstractTransition, w); + //// STARTUP OVERHEAD REASONING + // Since the transition logic is executed after the sync + // block and the DU, we need to attribute them to the head + // node of the next phase. + DagNode dagStartNodeOfNextPhase = dsFragment.getObjectFile().getDag().start; + // Add instructions for worker. + addInstructionSequenceForWorker(partialSchedules, w, dagStartNodeOfNextPhase, null, concreteTransition); } } + // Handling the default transition // Make sure to have the default transition copies to be appended LAST, // since default transitions are taken when no other transitions are taken. if (defaultTransition != null) { - for (int i = 0; i < workers; i++) { - partialSchedules - .get(i) - .addAll(replaceAbstractRegistersToConcreteRegisters(defaultTransition, i)); + for (int w = 0; w < workers; w++) { + List concreteTransition = replaceAbstractRegistersToConcreteRegisters(defaultTransition, w); + //// STARTUP OVERHEAD REASONING + // If the downstream fragment is EPILOGUE, which does not have + // object files. Set the associated DAG node to null. + DagNode dagStartNodeOfNextPhase; + if (defaultDownstreamFragment.getObjectFile() == null && defaultDownstreamFragment.getPhase() == Phase.EPILOGUE) { + dagStartNodeOfNextPhase = null; + } else if (defaultDownstreamFragment.getObjectFile() != null) { + dagStartNodeOfNextPhase = defaultDownstreamFragment.getObjectFile().getDag().start; + } else { + throw new RuntimeException("Unhandled phase without object files: " + defaultDownstreamFragment.getPhase()); + } + addInstructionSequenceForWorker(partialSchedules, w, dagStartNodeOfNextPhase, null, concreteTransition); } } @@ -1996,7 +2026,7 @@ private List> generateEpilogue(List nodes) { /** Generate the synchronization code block. */ private List> generateSyncBlock(List nodes) { - + System.out.println("*** Nodes: " + nodes); List> syncBlock = new ArrayList<>(); for (int w = 0; w < workers; w++) { diff --git a/core/src/main/java/org/lflang/analyses/pretvm/instructions/Instruction.java b/core/src/main/java/org/lflang/analyses/pretvm/instructions/Instruction.java index f084e01b0f..ccfda8ab71 100644 --- a/core/src/main/java/org/lflang/analyses/pretvm/instructions/Instruction.java +++ b/core/src/main/java/org/lflang/analyses/pretvm/instructions/Instruction.java @@ -85,8 +85,10 @@ public enum Opcode { /** Worker who owns this instruction */ private int worker; - /** DAG node for which this instruction is generated */ - private DagNode node; + /** A list of DAG nodes for which this instruction is generated. This + * is a list because an instruction can be generated for nodes in + * different phases. For example, a WU instruction in the sync block. */ + private List nodes = new ArrayList<>(); /** Getter of the opcode */ public Opcode getOpcode() { @@ -139,11 +141,17 @@ public void setWorker(int worker) { } public DagNode getDagNode() { - return this.node; + if (this.nodes.size() > 1) + throw new RuntimeException("This instruction is generated for more than one node!"); + return this.nodes.get(0); + } + + public List getDagNodes() { + return this.nodes; } public void setDagNode(DagNode node) { - this.node = node; + this.nodes.add(node); } @Override