blob: 43203861fca82ba1ee0953627c74e5d329da058d [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.Iterator;
import org.eclipse.app4mc.amalthea.model.AccessPrecedenceSpec;
import org.eclipse.app4mc.amalthea.model.ActivityGraphItem;
import org.eclipse.app4mc.amalthea.model.AffinityConstraint;
import org.eclipse.app4mc.amalthea.model.Amalthea;
import org.eclipse.app4mc.amalthea.model.AmaltheaFactory;
import org.eclipse.app4mc.amalthea.model.ConstraintsModel;
import org.eclipse.app4mc.amalthea.model.Label;
import org.eclipse.app4mc.amalthea.model.LabelAccess;
import org.eclipse.app4mc.amalthea.model.LabelAccessEnum;
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.RunnableEntityGroup;
import org.eclipse.app4mc.amalthea.model.RunnableOrderType;
import org.eclipse.app4mc.amalthea.model.RunnablePairingConstraint;
import org.eclipse.app4mc.amalthea.model.RunnableSequencingConstraint;
import org.eclipse.app4mc.amalthea.model.SWModel;
import org.eclipse.app4mc.multicore.partitioning.IParConstants;
import org.eclipse.app4mc.multicore.partitioning.utils.CreateTAInput;
import org.eclipse.app4mc.multicore.partitioning.utils.CycleElimination;
import org.eclipse.app4mc.multicore.partitioning.utils.Helper;
import org.eclipse.app4mc.multicore.sharelibs.OutputPathParser;
import org.eclipse.app4mc.multicore.sharelibs.UniversalHandler;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PerformPartitioning {
/**
* Partitioning using ESSP algorithm
*/
public static final String PART_ESSP = "essp";
/**
* Partitioning using CPP algorithm
*/
public static final String PART_CPP = "cpp";
private static final Logger LOGGER = LoggerFactory.getLogger(PerformPartitioning.class);
/**
* determines which operation shall be performed: activation analysis, label analysis, graph or independent graph partitioning and calls the corresponding
* classes
*/
public void execute(final IFile file, final IProgressMonitor monitor, final IPreferenceStore store) {
final UniversalHandler uh = UniversalHandler.getInstance();
final OutputPathParser outputParser = new OutputPathParser(IParConstants.PRE_LOC_RADIO, IParConstants.PRE_LOC_STRING, store);
String append = "";
if (!file.getName().endsWith("_PrePartitioned.amxmi")) {
append = "_PrePartitioned";
}
uh.dropCache();
uh.readModels(outputParser.parseOutputFileURI(file, append), true);
if (uh.getSwModel() == null) {
LOGGER.error("No SWModel found at {}", outputParser.parseOutputFileURI(file, append));
return;
}
final AmaltheaFactory af = AmaltheaFactory.eINSTANCE;
Amalthea amodels = af.createAmalthea();
assert uh.getSwModel() != null;
amodels = new Helper().setAllModels(amodels, uh);
if (amodels.getSwModel().getProcessPrototypes().isEmpty()) {
LOGGER.debug("!!No ProcessPrototypes present. Creating one containing all RunnableCalls.");
amodels.setSwModel(createPPs(amodels.getSwModel()));
}
switch (store.getString(IParConstants.PRE_PARTITIONING_ALG)) {
case PART_ESSP:
LOGGER.debug("**Starting earliest start schedule partitioning**");
final ESSP essp = new ESSP(amodels.getSwModel(), amodels.getConstraintsModel(), store.getInt(IParConstants.PRE_INT));
essp.build(monitor);
if (essp.getSwm().getProcessPrototypes().size() < store.getInt(IParConstants.PRE_INT)) {
LOGGER.debug(
"ESSP could not generate {} partitions since the softwaremodel's graph does not feature enough parallel runnables (though possibly sequences). {} Partitions could be generated.",
store.getInt(IParConstants.PRE_INT), essp.getSwm().getProcessPrototypes().size());
}
amodels.setConstraintsModel(essp.getCm());
amodels.setSwModel(essp.getSwm());
amodels = retainAffntyCnstrnts(file, amodels, monitor);
final String str = new Helper().writePPs(amodels.getSwModel().getProcessPrototypes());
LOGGER.debug(str);
LOGGER.debug("**Earliest start schedule partitioning finished**");
Helper.writeModelFile(outputParser.parseOutputFileURI(file, "_ESSP"), amodels);
break;
case PART_CPP:
LOGGER.debug("**Starting critical path partitioning**");
final CPP cpp = new CPP(amodels.getSwModel(), amodels.getConstraintsModel());
cpp.bglobalCP = store.getBoolean(IParConstants.PRE_GCP);
cpp.build(monitor);
amodels.setConstraintsModel(cpp.getCm());
amodels.setSwModel(cpp.getSwm());
amodels = retainAffntyCnstrnts(file, amodels, monitor);
final String str2 = new Helper().writePPs(amodels.getSwModel().getProcessPrototypes());
LOGGER.debug(str2);
LOGGER.debug("**Critical path partitioning finished**");
Helper.writeModelFile(outputParser.parseOutputFileURI(file, "_CPP"), amodels);
break;
default:
break;
}
if (store.getBoolean(IParConstants.PRE_TA)) {
LOGGER.debug("**Starting generating a runnable sequencing constraints alternative**");
final CreateTAInput ctai = new CreateTAInput();
ctai.setCm(amodels.getConstraintsModel());
ctai.combineSimilarRSCs();
amodels.setConstraintsModel(ctai.getCm());
Helper.writeModelFile(outputParser.parseOutputFileURI(file, "_TAInput"), amodels);
LOGGER.debug("**Generating a runnable sequencing constraints alternative finished**");
}
// print result
final long swmInstructionsCount = getSWMInstructions(amodels.getSwModel());
final long tmp = (long) ((float) getMaxPPLength(amodels.getSwModel()) / (swmInstructionsCount > 0 ? swmInstructionsCount : 1)
* 100);
LOGGER.debug(
"Sequential runtime of {} by {} Runnables were split into {} ProcessPrototypes with maximal {} Instructions to reduce overall execution time to {}% and by {}%.",
swmInstructionsCount, amodels.getSwModel().getRunnables().size(), amodels.getSwModel().getProcessPrototypes().size(),
getMaxPPLength(amodels.getSwModel()), tmp, (100 - tmp));
}
private static final String CR = "CumulatedRunnable";
/**
* retain cumulated runnables from affinity constraints: get original file, replace CumulatedRunnables with AffinityRunnables from original file (@param uh,
* file), add new rscs; @return Amalthea model with new RunnableSequencingConstraints
*/
public Amalthea retainAffntyCnstrnts(final IFile file, final Amalthea amodels, final IProgressMonitor monitor) {
final UniversalHandler uh = UniversalHandler.getInstance();
uh.dropCache();
if (file.getFullPath().toString().startsWith("/platform:/resource")) {
uh.readModels(URI.createPlatformResourceURI(file.getFullPath().toString().substring(19), true), true);
}
else {
uh.readModels(URI.createPlatformResourceURI(file.getFullPath().toString(), true), true);
}
if (uh.getConstraintsModel() != null && uh.getConstraintsModel().getAffinityConstraints() != null
&& !uh.getConstraintsModel().getAffinityConstraints().isEmpty()) {
/* add all original constraints except RunnableSequencingConstraints */
final EList<RunnableSequencingConstraint> rsclo = amodels.getConstraintsModel().getRunnableSequencingConstraints();
amodels.setConstraintsModel(uh.getConstraintsModel());
amodels.getConstraintsModel().getRunnableSequencingConstraints().addAll(rsclo);
/* create a list of all cumulatedRunnables */
final EList<Runnable> cumuRuns = new BasicEList<>();
for (final Runnable r : amodels.getSwModel().getRunnables()) {
if (r.getName().contains(CR)) {
cumuRuns.add(r);
}
}
/* add original runnables and update runnable calls */
amodels.setSwModel(
addOrgRunsUpdateRunCalls(amodels.getConstraintsModel().getAffinityConstraints(), amodels.getSwModel(), cumuRuns));
/* update runnable sequencing constraints */
amodels.setConstraintsModel(updateRSCs(amodels, cumuRuns));
/* delete RSCs to cumulated Runnables */
delRSCsToCumulatedRuns(amodels, cumuRuns);
/*
* check if there occur new cycles after cumulatedRunable dissolution
*/
final CycleElimination ce = new CycleElimination(amodels.getSwModel(), amodels.getConstraintsModel());
final IPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE,
FrameworkUtil.getBundle(getClass()).getSymbolicName());
ce.setparams(store);
while (!ce.run(monitor).isOK()) {
/* waiting for job to complete */
}
amodels.setConstraintsModel(ce.getCm());
amodels.setSwModel(ce.getSwm());
}
/* update first / last / activation ref for ProcessPrototypes */
amodels.setSwModel(new Helper().updatePPsFirstLastActParams(amodels.getSwModel()));
/* check if rscs all reference a processprototype */
amodels.setConstraintsModel(new Helper().updateRSCs(amodels.getConstraintsModel(), amodels.getSwModel()));
return amodels;
}
private void delRSCsToCumulatedRuns(final Amalthea amodels, final EList<Runnable> cumuRuns) {
final EList<RunnableSequencingConstraint> rrsc = new BasicEList<>();
for (final RunnableSequencingConstraint rsc : amodels.getConstraintsModel().getRunnableSequencingConstraints()) {
if (!rsc.getRunnableGroups().isEmpty() && !rsc.getRunnableGroups().get(0).getRunnables().isEmpty()
&& (cumuRuns.contains(rsc.getRunnableGroups().get(0).getRunnables().get(0))
|| cumuRuns.contains(rsc.getRunnableGroups().get(1).getRunnables().get(0)))) {
rrsc.add(rsc);
}
}
amodels.getConstraintsModel().getRunnableSequencingConstraints().removeAll(rrsc);
/* remove all temporary cumulatedRunnables */
amodels.getSwModel().getRunnables().removeAll(cumuRuns);
/* remove temporary cumulated runnables */
for (final ProcessPrototype pp : amodels.getSwModel().getProcessPrototypes()) {
if (!pp.getAccessPrecedenceSpec().isEmpty()) {
removeCumuRunAccessPrecSpec(pp);
}
}
}
private void removeCumuRunAccessPrecSpec(final ProcessPrototype pp) {
for (final Iterator<AccessPrecedenceSpec> it = pp.getAccessPrecedenceSpec().iterator(); it.hasNext();) {
final AccessPrecedenceSpec aps = it.next();
if (aps.getOrigin().getName().contains(CR) || aps.getTarget().getName().contains(CR)) {
it.remove();
}
}
}
/**
* From each entry in the @param cumulated Runnable list, originally separated runnables are dissolved, added to the @param swmodel and all runnable calls
* from the cummulated Runnables are replaced with the corresponding mutliple calls of the original Runnables (required for AffinityConstraints)
*
*/
private SWModel addOrgRunsUpdateRunCalls(final EList<AffinityConstraint> ac, final SWModel swm, final EList<Runnable> cumuRuns) {
for (final Runnable cr : cumuRuns) {
final int crindex = Integer.parseInt(cr.getName().substring(17, cr.getName().length()));
if (ac.get(crindex) instanceof RunnablePairingConstraint) {
/* add original runnables */
final RunnableEntityGroup reg = (RunnableEntityGroup) ((RunnablePairingConstraint) ac.get(crindex)).getGroup();
swm.getRunnables().addAll(reg.getRunnables());
/* Update RunnableCalls */
updateRunnableCalls(swm, cr, reg);
}
}
return swm;
}
private void updateRunnableCalls(final SWModel swm, final Runnable cr, final RunnableEntityGroup reg) {
for (final ProcessPrototype pp : swm.getProcessPrototypes()) {
final EList<RunnableCall> trcrem = new BasicEList<>();
final EList<RunnableCall> trcs = new BasicEList<>();
int index = 0;
for (final RunnableCall trc : pp.getRunnableCalls()) {
if (cr.equals(trc.getRunnable())) {
index = pp.getRunnableCalls().indexOf(trc);
final AmaltheaFactory af = AmaltheaFactory.eINSTANCE;
for (final Runnable or : reg.getRunnables()) {
/* create trcs for original runnables */
final RunnableCall ntrc = af.createRunnableCall();
ntrc.setRunnable(or);
trcs.add(ntrc);
}
trcrem.add(trc);
}
}
pp.getRunnableCalls().addAll(index, trcs);
/* remove cumulated runnable trcs */
pp.getRunnableCalls().removeAll(trcrem);
}
}
/**
* Runnable sequencing constraints (contained and @return in the @param cm constraints model) are updated to the original label accesses (required for
* affinityConstraints)
*
*/
private ConstraintsModel updateRSCs(final Amalthea am, final EList<Runnable> cumuRuns) {
for (final Runnable cr : cumuRuns) {
final int crindex = Integer.parseInt(cr.getName().substring(17, cr.getName().length()));
if (am.getConstraintsModel().getAffinityConstraints().get(crindex) instanceof RunnablePairingConstraint
&& (null == ((RunnablePairingConstraint) am.getConstraintsModel().getAffinityConstraints().get(crindex)).getTarget())) {
final RunnableEntityGroup reg = (RunnableEntityGroup) ((RunnablePairingConstraint) am.getConstraintsModel()
.getAffinityConstraints().get(crindex)).getGroup();
itrtGrps(am, reg);
}
}
return am.getConstraintsModel();
}
private void itrtGrps(final Amalthea am, final RunnableEntityGroup reg) {
for (final Runnable or : reg.getRunnables()) {
for (final ActivityGraphItem ri : or.getRunnableItems()) {
if (ri instanceof LabelAccess) {
createRSCRWoWR(am, or, ri);
}
}
}
}
private void createRSCRWoWR(final Amalthea am, final Runnable or, final ActivityGraphItem ri) {
final LabelAccess la = (LabelAccess) ri;
final Label l = la.getData();
if (la.getAccess().equals(LabelAccessEnum.READ)) {
/* or is target */
final EList<Runnable> wrs = getAccessingRunnables(l, LabelAccessEnum.WRITE, am.getSwModel());
crtRWRSC(am, or, wrs);
}
else if (la.getAccess().equals(LabelAccessEnum.WRITE)) {
/* or is source */
final EList<Runnable> rrs = getAccessingRunnables(l, LabelAccessEnum.READ, am.getSwModel());
for (final Runnable rr : rrs) {
if (!rr.getName().contains(CR)) {
final RunnableSequencingConstraint rsc = createRSC(or, rr, am.getSwModel());
if (!cMcontains(am.getConstraintsModel(), rsc) && null != rsc) {
am.getConstraintsModel().getRunnableSequencingConstraints().add(rsc);
}
}
}
}
}
private void crtRWRSC(final Amalthea am, final Runnable or, final EList<Runnable> wrs) {
for (final Runnable r : wrs) {
if (!r.getName().contains(CR)) {
final RunnableSequencingConstraint rsc = createRSC(r, or, am.getSwModel());
if (!cMcontains(am.getConstraintsModel(), rsc) && null != rsc) {
am.getConstraintsModel().getRunnableSequencingConstraints().add(rsc);
}
}
}
}
/**
* checks all accesses to a given label @param l and returns @return a list of runnables with the specific (@param lae) accesses from the given
* swmodel @param swm
*
*/
private EList<Runnable> getAccessingRunnables(final Label l, final LabelAccessEnum lae, final SWModel swm) {
final EList<Runnable> rl = new BasicEList<>();
for (final Runnable r : swm.getRunnables()) {
for (final ActivityGraphItem ri : r.getRunnableItems()) {
if (ri instanceof LabelAccess) {
final LabelAccess la = (LabelAccess) ri;
if (null == la.getData()) {
LOGGER.error("label access with no data found! at Runnable {}", r.getName());
}
if (la.getData().equals(l) && la.getAccess() == lae) {
rl.add(r);
}
}
}
}
return rl;
}
/**
* returns boolean whether a @param RunnableSequencingConstrainted is contained in the @param constraints model or not
*
*/
private boolean cMcontains(final ConstraintsModel cm, final RunnableSequencingConstraint rsc) {
for (final RunnableSequencingConstraint orsc : cm.getRunnableSequencingConstraints()) {
if (!rsc.getRunnableGroups().isEmpty() && !rsc.getRunnableGroups().get(0).getRunnables().isEmpty()
&& !orsc.getRunnableGroups().isEmpty() && !orsc.getRunnableGroups().get(0).getRunnables().isEmpty()) {
if (orsc.getRunnableGroups().get(0).getRunnables().get(0).equals(rsc.getRunnableGroups().get(0).getRunnables().get(0))
&& orsc.getRunnableGroups().get(1).getRunnables().get(0)
.equals(rsc.getRunnableGroups().get(1).getRunnables().get(0))) {
return true;
}
}
else if (null != rsc.getName()) {
LOGGER.error("RSC empty entries: {} ", rsc.getName());
}
}
return false;
}
/**
* @return a RunnableSequencingConstraint, having two groups. First group contains @param source, second group contains @param target
*/
private RunnableSequencingConstraint createRSC(final Runnable source, final Runnable target, final SWModel swm) {
if (null == target || null == source) {
LOGGER.debug("RSC cannot be created for null Runnable");
return null;
}
final AmaltheaFactory factory = AmaltheaFactory.eINSTANCE;
final RunnableSequencingConstraint rsc = factory.createRunnableSequencingConstraint();
rsc.setName(source.getName() + "-->" + target.getName());
final RunnableEntityGroup prg1 = factory.createRunnableEntityGroup();
final RunnableEntityGroup prg2 = factory.createRunnableEntityGroup();
final ProcessPrototype sourcePP = new Helper().getPPfromR(source, swm);
rsc.getProcessScope().add(sourcePP);
prg1.getRunnables().add(source);
prg2.getRunnables().add(target);
rsc.getRunnableGroups().add(prg1);
rsc.getRunnableGroups().add(prg2);
rsc.setOrderType(RunnableOrderType.SUCCESSOR);
return rsc;
}
/**
* @return max instructions sum of the longest ProcessPrototype
*/
private long getMaxPPLength(final SWModel swm) {
long ret = 0;
for (final ProcessPrototype pp : swm.getProcessPrototypes()) {
long temp = 0;
for (final RunnableCall trc : pp.getRunnableCalls()) {
temp += new Helper().getInstructions(trc.getRunnable());
}
if (temp > ret) {
ret = temp;
}
}
return ret;
}
/**
* @return sum of all instructions of the @param swmodel
*/
private long getSWMInstructions(final SWModel swm) {
long ret = 0;
for (final Runnable r : swm.getRunnables()) {
ret += new Helper().getInstructions(r);
}
return ret;
}
/**
* @return swmodel containing a temporary ProcessPrototype with RunnableCalls for all Runnables within the @param swmodel
*/
private SWModel createPPs(final SWModel swm) {
final AmaltheaFactory af = AmaltheaFactory.eINSTANCE;
final ProcessPrototype pp = af.createProcessPrototype();
if (swm.getRunnables().get(0).getFirstActivation() != null) {
pp.setActivation(swm.getRunnables().get(0).getFirstActivation());
}
pp.setName("PPTemp");
for (final Runnable r : swm.getRunnables()) {
final RunnableCall trc = af.createRunnableCall();
trc.setRunnable(r);
pp.getRunnableCalls().add(trc);
}
swm.getProcessPrototypes().add(pp);
return swm;
}
}