blob: 88b6c302d7230278e854b6c16e64ada2f6b29c17 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017-2020 Dortmund University of Applied Sciences and Arts and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Dortmund University of Applied Sciences and Arts - initial API and implementation
*******************************************************************************/
package org.eclipse.app4mc.multicore.partitioning.algorithms;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.app4mc.amalthea.model.AmaltheaFactory;
import org.eclipse.app4mc.amalthea.model.ConstraintsModel;
import org.eclipse.app4mc.amalthea.model.ProcessPrototype;
import org.eclipse.app4mc.amalthea.model.Runnable;
import org.eclipse.app4mc.amalthea.model.RunnableCall;
import org.eclipse.app4mc.amalthea.model.RunnableSequencingConstraint;
import org.eclipse.app4mc.amalthea.model.SWModel;
import org.eclipse.app4mc.multicore.partitioning.utils.Helper;
import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* GGP means Global Graph Partitioning. The GGP approach identifies independent graphs and creates ProcessPrototypes for each graph.
*/
public class GGP {
private static final Logger LOGGER = LoggerFactory.getLogger(GGP.class);
private final SWModel swm;
private final ConstraintsModel cm;
private LinkedList<Runnable> unassigned = new LinkedList<>();
LinkedList<LinkedList<Runnable>> tasks = new LinkedList<>();
private final LinkedList<Runnable> visited = new LinkedList<>();
private final LinkedList<Runnable> unvisited = new LinkedList<>();
private DirectedAcyclicGraph<Runnable, RunnableSequencingConstraint> graph = null;
GGP(final SWModel swm, final LinkedList<Runnable> uCNs, final LinkedList<LinkedList<Runnable>> threads, final ConstraintsModel cm) {
this.swm = swm;
this.unassigned = uCNs;
this.tasks = threads;
this.cm = cm;
}
public GGP(final SWModel swm, final ConstraintsModel cm) {
this.cm = cm;
this.swm = swm;
for (final Runnable r : swm.getRunnables()) {
this.unassigned.add(r);
this.unvisited.add(r);
}
}
public GGP(final SWModel swm, final ConstraintsModel cm, final DirectedAcyclicGraph<Runnable, RunnableSequencingConstraint> graph) {
this.cm = cm;
this.swm = swm;
this.graph = graph;
}
/**
* starts the independent graph identification and stores its outcome in the SWModel swm as prototypes
*/
public void build() {
if (this.unassigned.isEmpty()) {
this.unassigned.addAll(this.swm.getRunnables());
}
else if (this.unassigned.size() != this.swm.getRunnables().size()) {
LOGGER.debug("Unassigned != Runnables.size");
}
if (this.swm.getProcessPrototypes().isEmpty()) {
final AmaltheaFactory swf = AmaltheaFactory.eINSTANCE;
final ProcessPrototype pp = swf.createProcessPrototype();
pp.setName("AllRunnables");
for (final Runnable r : this.swm.getRunnables()) {
final RunnableCall trc = swf.createRunnableCall();
trc.setRunnable(r);
pp.getRunnableCalls().add(trc);
}
this.swm.getProcessPrototypes().add(pp);
}
this.graph = new CriticalPath(getSwm(), getCm()).getGraph();
for (final ProcessPrototype pp : this.swm.getProcessPrototypes()) {
genGraphs(pp);
}
final StringBuilder sb = new StringBuilder();
int ct = 0;
for (final LinkedList<Runnable> lrl : this.tasks) {
sb.append(ct++ + ": ");
for (final Runnable r : lrl) {
sb.append(r.getName() + ", ");
}
sb.append("\n");
}
correctPPs();
}
/**
* This method identifies the first unassigned source runnable of a ProcessPrototype, and returns the corresponding graph. In case there are multiple
* independent graphs within a ProcessPrototype, the method recursively assigns all graphs to the global thread list begnning with the biggest graph untill
* the smallest graph is returned. In case there is no graph but loose runnables only, the method returns a list of all loose runnables according to the
* ProcessPrototye.
*
* @throws Exception
**/
private void genGraphs(final ProcessPrototype pp) {
final List<Runnable> sinks = getSinks(pp);
if (sinks.isEmpty()) {
LOGGER.debug("getGraph didnt find unhandled Runnable at {}; TRCs {}", pp.getName(), pp.getRunnableCalls().size());
return;
}
for (final Runnable r : sinks) {
if (this.unassigned.contains(r)) {
final Set<Runnable> task = getCoherentNodes(r, pp);
this.unassigned.removeAll(task);
final LinkedList<Runnable> temp = new LinkedList<>();
temp.addAll(task);
this.tasks.add(temp);
}
}
}
private List<Runnable> getSinks(final ProcessPrototype pp) {
final List<Runnable> sinks = getSinks();
final Set<Runnable> rs = new HashSet<>();
for (final RunnableCall trc : pp.getRunnableCalls()) {
rs.add(trc.getRunnable());
}
sinks.retainAll(rs);
return sinks;
}
public List<Runnable> getSinks() {
final List<Runnable> sinks = new LinkedList<>();
for (final Runnable r : this.swm.getRunnables()) {
if (this.graph.outgoingEdgesOf(r).isEmpty()) {
sinks.add(r);
}
else {
final Set<RunnableSequencingConstraint> oe = this.graph.outgoingEdgesOf(r);
int ct = 0;
for (final RunnableSequencingConstraint rsc : oe) {
final Runnable target = this.graph.getEdgeTarget(rsc);
if (!new Helper().getPPfromR(r, this.swm).equals(new Helper().getPPfromR(target, this.swm))) {
ct++;
}
}
if (oe.size() == ct) {
sinks.add(r);
}
}
}
if (sinks.isEmpty()) {
LOGGER.error("No sink found");
}
return sinks;
}
/**
* Looks for all Runnables within the ProcessPrototype @param ppi, that precede or succeed the runnable @param r according to dependencies
*
*/
private Set<Runnable> getCoherentNodes(final Runnable r, final ProcessPrototype pp) {
final Set<Runnable> rl = new HashSet<>();
rl.add(r);
this.visited.add(r);
final Iterator<RunnableSequencingConstraint> i = this.graph.incomingEdgesOf(r).iterator();
while (i.hasNext()) {
final RunnableSequencingConstraint rsc = i.next();
final Runnable source = this.graph.getEdgeSource(rsc);
if (new Helper().getPPfromR(source, this.swm).equals(pp) && !this.visited.contains(source)) {
rl.addAll(getCoherentNodes(source, pp));
}
}
final Iterator<RunnableSequencingConstraint> i2 = this.graph.outgoingEdgesOf(r).iterator();
while (i2.hasNext()) {
final RunnableSequencingConstraint rsc = i2.next();
final Runnable target = this.graph.getEdgeTarget(rsc);
if (new Helper().getPPfromR(target, this.swm).equals(pp) && !this.visited.contains(target)) {
rl.addAll(getCoherentNodes(target, pp));
}
}
return rl;
}
public SWModel getSwm() {
return this.swm;
}
public ConstraintsModel getCm() {
return this.cm;
}
/**
* creates PPs, TRCs, PPname, PPactivation, PPfirstrunnable, PPlastrunnable requires this.tasks
*/
private void correctPPs() {
this.swm.getProcessPrototypes().clear();
int i = 0;
for (final LinkedList<Runnable> llr : this.tasks) {
final AmaltheaFactory instance = AmaltheaFactory.eINSTANCE;
final ProcessPrototype pp = instance.createProcessPrototype();
for (final Runnable r : llr) {
final RunnableCall trc = instance.createRunnableCall();
trc.setRunnable(r);
pp.getRunnableCalls().add(trc);
}
pp.setName("PP" + i++);
pp.setActivation(pp.getRunnableCalls().get(0).getRunnable().getFirstActivation());
pp.setFirstRunnable(llr.get(0));
pp.setLastRunnable(llr.get(llr.size() - 1));
this.swm.getProcessPrototypes().add(pp);
}
/* update PP references in constraints model */
for (final RunnableSequencingConstraint rsc : this.cm.getRunnableSequencingConstraints()) {
setProcessScopes(rsc);
}
}
private void setProcessScopes(final RunnableSequencingConstraint rsc) {
final Runnable source = rsc.getRunnableGroups().get(0).getRunnables().get(0);
final Runnable target = rsc.getRunnableGroups().get(1).getRunnables().get(0);
if (!this.swm.getRunnables().contains(source) || !this.swm.getRunnables().contains(target)) {
LOGGER.debug("Runnable {} / {} could not be found. SWM.size {}", source.getName(), target.getName(),
this.swm.getRunnables().size());
}
for (final ProcessPrototype pp : this.swm.getProcessPrototypes()) {
for (final RunnableCall trc : pp.getRunnableCalls()) {
if (trc.getRunnable().equals(source) || trc.getRunnable().equals(target)) {
rsc.getProcessScope().clear();
rsc.getProcessScope().add(pp);
}
}
}
}
}