blob: 20d05560b97b39bab5bb875fbc5e1ac29b8cf341 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2019 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.execution.logic.openmapping;
import java.math.BigInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.stream.Collectors;
import org.eclipse.app4mc.amalthea.model.MappingModel;
import org.eclipse.app4mc.amalthea.model.ProcessingUnit;
import org.eclipse.app4mc.amalthea.model.Scheduler;
import org.eclipse.app4mc.amalthea.model.SchedulerAllocation;
import org.eclipse.app4mc.amalthea.model.Task;
import org.eclipse.app4mc.amalthea.model.TaskAllocation;
public class OMUtil {
private OMUtil() {
}
/**
* Create OMMapping from an AMALTHEA MappingModel object.
*
* @param model
* @return
*/
public static OMMapping createOMMapping(final MappingModel model) {
final List<TaskAllocation> allocations = model.getTaskAllocation();
final List<SchedulerAllocation> schedAllocs = model.getSchedulerAllocation();
final Map<ProcessingUnit, OMCore> cores = new HashMap<>();
final Map<Scheduler, ProcessingUnit> schedToCore = new HashMap<>();
for (final SchedulerAllocation sa : schedAllocs) {
final ProcessingUnit c = sa.getResponsibility().get(0);
final Scheduler s = sa.getScheduler();
schedToCore.put(s, c);
}
final OMMapping m = new OMMapping();
for (final TaskAllocation ta : allocations) {
final Task t = ta.getTask();
final Scheduler s = ta.getScheduler();
final ProcessingUnit c = schedToCore.get(s);
if (!cores.containsKey(c)) {
cores.put(c, new OMCore(c));
}
final OMAllocation alloc = new OMAllocation(new OMTask(t), cores.get(c));
m.addAllocation(alloc);
}
return m;
}
/**
* Restructure the privieded OMAllocation to a Java-Map with cores as keys
* and task-list as values.
*
* @param list
* @return
*/
public static Map<OMCore, List<OMTask>> getCoreTaskMap(final List<OMAllocation> list) {
return list.stream().collect(Collectors.groupingBy(OMAllocation::getCore,
Collectors.mapping(OMAllocation::getTask, Collectors.toList())));
}
/**
* Get the utilization of the core if the provided task-list runs on it.
*
* @param c
* @param tasks
* @return utilization (1 is 100% utilization)
*/
public static double getUtilization(final OMCore c, final List<OMTask> tasks) throws MalformedModelException {
final DoubleAdder d = new DoubleAdder();
for (final OMTask t : tasks) {
if (t.getInstructionCount() <= 0) {
throw new MalformedModelException(
"Task " + t.getTaskRef().getName() + " instruction-count is lesser equal zero!");
}
d.add(((double) getProcessingTime(c, t.getInstructionCount())) / t.getPeriod());
}
return d.doubleValue();
}
/**
* Get the major cycle of the provided task-list. Also known as
* hyper-period.
*
* @param tasks
* @return
*/
public static long getMajorCycle(final List<OMTask> tasks) {
return lcm(tasks.parallelStream().map(OMTask::getPeriod).collect(Collectors.toList()));
}
/**
* Get least common multiple of all passed values.
*
* @param values
* @return
*/
private static long lcm(final List<Long> values) {
assert values != null;
assert 1 >= values.size();
final long result = values.get(0);
final long[] longArr = new long[values.size()];
int i = 0;
for (final Long v : values) {
longArr[i] = v;
i++;
}
lcm(longArr);
return result;
}
private static long lcm(final long a, final long b) {
return a * (b / gcd(a, b));
}
private static long lcm(final long[] input) {
long result = input[0];
for (int i = 1; i < input.length; i++) {
result = lcm(result, input[i]);
}
return result;
}
private static long gcd(long a, long b) {
while (b > 0) {
final long temp = b;
b = a % b;
a = temp;
}
return a;
}
/**
* Adds the instructions of the runnable call sequence from the provided
* task up to the last occurrence of the passed runnable.
*
* @param taskOfPre
* @param ompre
* @return
*/
private static long getReleaseInstructions(final OMTask taskOfPre, final OMRunnable ompre) {
long overallInstructions = 0;
long releaseInstructions = 0;
for (final OMRunnable r : taskOfPre.getRunnableCallSequence()) {
overallInstructions += r.getInstructionCount();
if (r == ompre) {
releaseInstructions = overallInstructions;
}
}
return releaseInstructions;
}
/**
* Get the time how long the provided core need to process the passed
* instruction-count.
*
* @param core
* @param instuctionCount
* @return time in pico-seconds.
* @throws MalformedModelException
*/
public static long getProcessingTime(final OMCore core, final long instuctionCount) throws MalformedModelException {
final BigInteger ips = BigInteger.valueOf(core.getFrequencyHz());
// Number of Instructions
final BigInteger ins = BigInteger.valueOf(instuctionCount);
// Piko-Seconds per Second
final BigInteger psps = BigInteger.valueOf(1000L * 1000L * 1000L * 1000L);
final BigInteger computationTime = psps.multiply(ins).divide(ips);
return computationTime.longValueExact();
}
/**
* Check if the provided graph is acyclic. Therefore the graph gets
* topologically sorted. If it can't be sorted its cyclic.
*
* @param edges
* @return
*/
public static boolean isDAG(final Collection<OMEdge> edges) {
return getTopologicallySoretedTasks(edges) != null;
}
/**
* Create a list of tasks from the provided task-graph by topologically sort
* it.
*
* @param edges
* @return sorted series of tasks or null if the graph is cyclic and can't
* be sorted.
*/
public static List<OMTask> getTopologicallySoretedTasks(final Collection<OMEdge> edges) {
final List<OMTask> resultList = new LinkedList<>();
final Map<OMTask, Set<OMTask>> predecessorMap = new HashMap<>(); // get
// all
// predecessor
// from
// successor
for (final OMEdge e : edges) {
/* put all task to map at first occurrence */
if (!predecessorMap.containsKey(e.getPost())) {
// nachfolger nicht in map
predecessorMap.put(e.getPost(), new HashSet<>());
}
if (!predecessorMap.containsKey(e.getPre())) {
predecessorMap.put(e.getPre(), new HashSet<>());
}
/*
* add predecessor task to predecessor list of successor (post task)
*/
predecessorMap.get(e.getPost()).add(e.getPre());
}
while (predecessorMap.size() > 0) {
OMTask curTask = null;
for (final Map.Entry<OMTask, Set<OMTask>> t : predecessorMap.entrySet()) {
if (t.getValue().isEmpty()) {
curTask = t.getKey();
break;
}
}
if (curTask == null) {
// all remaining tasks have predecessors => cyclic graph
return null;
}
resultList.add(curTask);
predecessorMap.remove(curTask);
// remove it as predecessor
for (final Set<OMTask> list : predecessorMap.values()) {
list.remove(curTask);
}
}
return resultList;
}
/**
* Search the corresponding core for the provided task in the mapping.
*
* @param mapping
* @param t
* @return core where the task runs on or null if not found.
*/
public static OMCore getCoreForTask(final OMMapping mapping, final OMTask t) {
for (final OMAllocation alloc : mapping.getAllocationList()) {
if (alloc.getTask() == t) {
return alloc.getCore();
}
}
return null;
}
}