diff --git b/.classpath a/.classpath new file mode 100644 index 0000000..f0257c5 --- /dev/null +++ a/.classpath @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git b/.gitignore a/.gitignore new file mode 100644 index 0000000..b57e62e --- /dev/null +++ a/.gitignore @@ -0,0 +1,3 @@ +/.vscode +/.settings +/target \ No newline at end of file diff --git b/.project a/.project new file mode 100644 index 0000000..e0c919e --- /dev/null +++ a/.project @@ -0,0 +1,23 @@ + + + jester + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git b/pom.xml a/pom.xml new file mode 100644 index 0000000..f65c9bb --- /dev/null +++ a/pom.xml @@ -0,0 +1,93 @@ + + 4.0.0 + mvd.jester + jester + jar + 1.0-SNAPSHOT + jester + http://maven.apache.org + + + UTF-8 + 1.8 + 1.8 + + + + + org.junit.jupiter + junit-jupiter-api + 5.4.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.4.0 + test + + + org.mockito + mockito-junit-jupiter + 2.21.0 + test + + + com.google.code.gson + gson + 2.2.4 + + + com.google.guava + guava + 28.0-jre + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git b/src/main/java/mvd/jester/App.java a/src/main/java/mvd/jester/App.java new file mode 100644 index 0000000..0c8d099 --- /dev/null +++ a/src/main/java/mvd/jester/App.java @@ -0,0 +1,24 @@ +package mvd.jester; + +import mvd.jester.model.Task; +import mvd.jester.model.SystemSetup; +import mvd.jester.tests.MaiaBertogna; +import mvd.jester.tests.SchmidMottok; +import mvd.jester.tests.TestEnvironment; + +/** + * Hello world! + * + */ +public class App { + public static void main(String[] args) { + + TestEnvironment te = new TestEnvironment(4000, 4); + + te.registerTestInterface(SchmidMottok.class); + te.registerTestInterface(MaiaBertogna.class); + + te.runTests(); + + } +} diff --git b/src/main/java/mvd/jester/model/Segment.java a/src/main/java/mvd/jester/model/Segment.java new file mode 100644 index 0000000..492cdf3 --- /dev/null +++ a/src/main/java/mvd/jester/model/Segment.java @@ -0,0 +1,51 @@ +package mvd.jester.model; + +import com.google.common.math.LongMath; + +public class Segment { + + private final long jobWcet; + private final long numberOfJobs; + private final long taskletWcet; + private final long numberOfTasklets; + + public Segment(long jobWcet, long numberOfJobs) { + this.jobWcet = jobWcet; + this.numberOfJobs = numberOfJobs; + if (numberOfJobs == 1) { + this.taskletWcet = this.jobWcet; + this.numberOfTasklets = this.numberOfJobs; + } else { + this.taskletWcet = LongMath.gcd(jobWcet, numberOfJobs); + this.numberOfTasklets = this.jobWcet * this.numberOfJobs / this.taskletWcet; + } + } + + /** + * @return the numberOfJobs + */ + public long getNumberOfJobs() { + return numberOfJobs; + } + + /** + * @return the jobWcet + */ + public long getJobWcet() { + return jobWcet; + } + + /** + * @return the numberOfTasklets + */ + public long getNumberOfTasklets() { + return numberOfTasklets; + } + + /** + * @return the taskletWcet + */ + public long getTaskletWcet() { + return taskletWcet; + } +} diff --git b/src/main/java/mvd/jester/model/SortedTaskSet.java a/src/main/java/mvd/jester/model/SortedTaskSet.java new file mode 100644 index 0000000..0c08913 --- /dev/null +++ a/src/main/java/mvd/jester/model/SortedTaskSet.java @@ -0,0 +1,16 @@ +package mvd.jester.model; + +import java.util.TreeSet; +import mvd.jester.priority.PriorityManager; + +/** + * SortedTaskSet + */ +public class SortedTaskSet extends TreeSet { + + private static final long serialVersionUID = 4808544133562675597L; + + public SortedTaskSet(PriorityManager priorityMananger) { + super((t1, t2) -> priorityMananger.compare(t1, t2)); + } +} diff --git b/src/main/java/mvd/jester/model/SystemSetup.java a/src/main/java/mvd/jester/model/SystemSetup.java new file mode 100644 index 0000000..55572e9 --- /dev/null +++ a/src/main/java/mvd/jester/model/SystemSetup.java @@ -0,0 +1,164 @@ +package mvd.jester.model; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; +import mvd.jester.priority.PriorityManager; +import mvd.jester.priority.RateMonotonic; + +/** + * TaskSet + */ +public class SystemSetup { + private SortedTaskSet tasks; + private final long numberOfProcessors; + + public SystemSetup(SortedTaskSet tasks, long numberOfProcessors) { + this.tasks = tasks; + this.numberOfProcessors = numberOfProcessors; + } + + /** + * @return the tasks + */ + public SortedTaskSet getTasks() { + return tasks; + } + + public void setSortedTaskSet(SortedTaskSet tasks) { + this.tasks = tasks; + } + + /** + * @return the numberOfProcessors + */ + public long getNumberOfProcessors() { + return numberOfProcessors; + } + + public double getUtilization() { + double utilization = 0; + for (Task t : tasks) { + utilization += (double) t.getMaximumWcet() / t.getPeriod(); + } + + return utilization; + } + + public static class Generator { + private long numberOfProcessors = 4; + private long minPeriod = 100; + private long maxPeriod = 1000; + private long minNumberOfSegments = 3; + private long maxNumberOfSegments = 7; + private long minNumberOfJobs = 2; + private long maxNumberOfJobs = 3 * numberOfProcessors / 2; + private long minWcet = 1; + private long ratio = randomTaskRatio(); + private PriorityManager priorityManager = new RateMonotonic(); + + public Generator() { + + } + + private long randomTaskPeriod() { + return ThreadLocalRandom.current().nextLong(minPeriod, maxPeriod); + } + + private long randomTaskRatio() { + return ThreadLocalRandom.current().nextLong(0, 100); + } + + private long randomNumberOfSegments() { + return ThreadLocalRandom.current().nextLong(minNumberOfSegments, maxNumberOfSegments); + } + + private long randomNumberOfJobs() { + return ThreadLocalRandom.current().nextLong(minNumberOfJobs, maxNumberOfJobs); + } + + private long randomWcet(long period, long numberOfSegments) { + long maxWcet = period / numberOfSegments; + + return ThreadLocalRandom.current().nextLong(minWcet, maxWcet); + } + + private Task generateTask() { + boolean serial = randomTaskRatio() > this.ratio; + long period = randomTaskPeriod(); + long numberOfSegments = 1; + Set segments = new LinkedHashSet(); + if (!serial) { + numberOfSegments = randomNumberOfSegments(); + } else { + numberOfSegments = 1; + } + for (int i = 0; i < numberOfSegments; ++i) { + serial = i % 2 == 0; + long wcet = randomWcet(period, numberOfSegments); + long numberOfJobs = serial ? 1 : randomNumberOfJobs(); + segments.add(new Segment(wcet, numberOfJobs)); + } + return new Task(period, segments); + } + + private SortedTaskSet generateTaskSet() { + SortedTaskSet taskSet = new SortedTaskSet(priorityManager); + + for (int i = 0; i < 4; ++i) { + Task task = generateTask(); + taskSet.add(task); + } + + return taskSet; + } + + public SystemSetup build() { + this.ratio = randomTaskRatio(); + SortedTaskSet taskSet = generateTaskSet(); + return new SystemSetup(taskSet, numberOfProcessors); + } + + public void addTask(SystemSetup taskSet) { + taskSet.tasks.add(generateTask()); + } + + /** + * @param numberOfProcessors the numberOfProcessors to set + */ + public void setNumberOfProcessors(long numberOfProcessors) { + this.numberOfProcessors = numberOfProcessors; + } + + + public Generator setNumberOfSegments(long minNumberOfSegments, long maxNumberOfSegments) { + this.minNumberOfSegments = minNumberOfSegments; + this.maxNumberOfSegments = maxNumberOfSegments; + + return this; + } + + public Generator setPeriods(long minPeriod, long maxPeriod) { + this.minPeriod = minPeriod; + this.maxPeriod = maxPeriod; + + return this; + } + + public Generator setPriorityManager(PriorityManager priorityManager) { + this.priorityManager = priorityManager; + return this; + } + + /** + * @param maxNumberOfJobs the maxNumberOfJobs to set + */ + public Generator setNumberOfJobs(long minNumberOfJobs, long maxNumberOfJobs) { + this.minNumberOfJobs = minNumberOfJobs; + this.maxNumberOfJobs = maxNumberOfJobs; + return this; + } + + } + +} diff --git b/src/main/java/mvd/jester/model/Task.java a/src/main/java/mvd/jester/model/Task.java new file mode 100644 index 0000000..7a1fac7 --- /dev/null +++ a/src/main/java/mvd/jester/model/Task.java @@ -0,0 +1,73 @@ +package mvd.jester.model; + +import java.util.Set; + +/** + * Task + */ +public class Task { + + private final long deadline; + private final long period; + private final Set segments; + private final long maximumWcet; + private final long maximumParallelism; + + public Task(long period, long deadline, Set segments) { + this.deadline = deadline; + this.period = period; + this.segments = segments; + + long maxWcet = 0; + long maxParallelism = 0; + for (Segment s : segments) { + maxWcet += s.getJobWcet() * s.getNumberOfJobs(); + if (maxParallelism < s.getNumberOfJobs()) { + maxParallelism = s.getNumberOfJobs(); + } + } + + this.maximumParallelism = maxParallelism; + this.maximumWcet = maxWcet; + } + + public Task(long period, Set segments) { + this(period, period, segments); + } + + + /** + * @return the deadline + */ + public long getDeadline() { + return deadline; + } + + /** + * @return the period + */ + public long getPeriod() { + return period; + } + + /** + * @return the segments + */ + public Set getSegments() { + return segments; + } + + /** + * @return the maximumWcet + */ + public long getMaximumWcet() { + return maximumWcet; + } + + /** + * @return the maximumParallelism + */ + public long getMaximumParallelism() { + return maximumParallelism; + } +} diff --git b/src/main/java/mvd/jester/priority/PriorityManager.java a/src/main/java/mvd/jester/priority/PriorityManager.java new file mode 100644 index 0000000..273b0ac --- /dev/null +++ a/src/main/java/mvd/jester/priority/PriorityManager.java @@ -0,0 +1,12 @@ +package mvd.jester.priority; + +import java.util.Comparator; +import mvd.jester.model.Task; + +/** + * PriorityManager + */ +public interface PriorityManager extends Comparator { + +} + diff --git b/src/main/java/mvd/jester/priority/RateMonotonic.java a/src/main/java/mvd/jester/priority/RateMonotonic.java new file mode 100644 index 0000000..d650e1b --- /dev/null +++ a/src/main/java/mvd/jester/priority/RateMonotonic.java @@ -0,0 +1,19 @@ +package mvd.jester.priority; + +import mvd.jester.model.Task; + +public class RateMonotonic implements PriorityManager { + + /** + * Compare the priority of two tasks according to the Rate Monotonic policy + * + * @param t1 The first task + * @param t2 The second task + * @return 0 if both tasks have the same priority, positive number if the first task has a + * higher priority, negative number if the second task has a higher priority + */ + @Override + public int compare(Task t1, Task t2) { + return Long.compare(t1.getPeriod(), t2.getPeriod()); + } +} diff --git b/src/main/java/mvd/jester/tests/AbstractTest.java a/src/main/java/mvd/jester/tests/AbstractTest.java new file mode 100644 index 0000000..1614be2 --- /dev/null +++ a/src/main/java/mvd/jester/tests/AbstractTest.java @@ -0,0 +1,17 @@ +package mvd.jester.tests; + +import java.util.HashMap; +import java.util.Map; +import mvd.jester.model.Task; + +/** + * AbstractTest + */ +public abstract class AbstractTest implements TestInterface { + + protected Map responseTimes; + + public AbstractTest() { + this.responseTimes = new HashMap(); + } +} diff --git b/src/main/java/mvd/jester/tests/MaiaBertogna.java a/src/main/java/mvd/jester/tests/MaiaBertogna.java new file mode 100644 index 0000000..a738e96 --- /dev/null +++ a/src/main/java/mvd/jester/tests/MaiaBertogna.java @@ -0,0 +1,121 @@ +package mvd.jester.tests; + +import java.math.RoundingMode; +import java.util.Map.Entry; +import com.google.common.math.LongMath; +import mvd.jester.model.Segment; +import mvd.jester.model.Task; +import mvd.jester.model.SystemSetup; + +/** + * MaiaBertogna + */ +public class MaiaBertogna extends AbstractTest { + + private SystemSetup systemSetup; + + public MaiaBertogna() { + } + + @Override + public boolean runSchedulabilityCheck(SystemSetup systemSetup) { + responseTimes.clear(); + this.systemSetup = systemSetup; + for (Task t : systemSetup.getTasks()) { + responseTimes.put(t, calculateResponseTime(t)); + } + + for (Entry keyValuePair : responseTimes.entrySet()) { + if (keyValuePair.getKey().getDeadline() < keyValuePair.getValue().longValue()) { + return false; + } + } + + return true; + } + + @Override + public String getName() { + return "MaiaBertogna"; + } + + private long calculateResponseTime(Task task) { + long minimumWcet = getMinimumWcet(task); + long responseTime = minimumWcet; + long previousResponseTime = 0; + + do { + previousResponseTime = responseTime; + long interference = 0; + + for (Task t : systemSetup.getTasks()) { + if (t.getPeriod() < task.getPeriod()) { + long maxNumberOfJobsOfT = t.getMaximumParallelism(); + for (int p = 0; p < maxNumberOfJobsOfT; ++p) { + interference += Math.min(getTaskInterference(t, responseTime, p + 1), + responseTime - minimumWcet + 1); + } + } + } + + double taskInterference = (double) interference / systemSetup.getNumberOfProcessors(); + long selfInterference = 0; + + long maxNumberOfJobs = task.getMaximumParallelism(); + for (int p = 0; p < maxNumberOfJobs; ++p) { + selfInterference += + Math.min(getSelfInterference(task, p + 1), responseTime - minimumWcet + 1); + } + + long totalInterference = (long) Math.floor(taskInterference + selfInterference); + + responseTime = minimumWcet + totalInterference; + } while (previousResponseTime != responseTime); + + + return responseTime; + } + + private long getSelfInterference(Task task, long parallelism) { + long interference = 0; + + for (Segment s : task.getSegments()) { + if (s.getNumberOfJobs() >= parallelism + 1) { + s.getJobWcet(); + } + } + + return interference; + } + + private long getTaskInterference(Task task, long interval, long parallelism) { + long responseTime = responseTimes.get(task); + long minWcet = getMinimumWcet(task); + long period = task.getPeriod(); + long numberOfJobs = + (LongMath.divide(interval + responseTime - minWcet, period, RoundingMode.FLOOR) + + 1); + + long workload = 0; + + for (Segment s : task.getSegments()) { + if (s.getNumberOfJobs() >= parallelism) { + workload += s.getJobWcet(); + } + } + + long interference = numberOfJobs * workload; + + return interference; + } + + private long getMinimumWcet(Task task) { + long minWcet = 0; + for (Segment s : task.getSegments()) { + minWcet += s.getJobWcet(); + } + + return minWcet; + } + +} diff --git b/src/main/java/mvd/jester/tests/SchmidMottok.java a/src/main/java/mvd/jester/tests/SchmidMottok.java new file mode 100644 index 0000000..44fc48f --- /dev/null +++ a/src/main/java/mvd/jester/tests/SchmidMottok.java @@ -0,0 +1,112 @@ +package mvd.jester.tests; + +import java.math.RoundingMode; +import java.util.Map.Entry; +import com.google.common.math.LongMath; +import mvd.jester.model.Segment; +import mvd.jester.model.Task; +import mvd.jester.model.SystemSetup; + +/** + * SchmidMottok + */ +public class SchmidMottok extends AbstractTest { + + private SystemSetup systemSetup; + + public SchmidMottok() { + } + + @Override + public boolean runSchedulabilityCheck(SystemSetup systemSetup) { + responseTimes.clear(); + this.systemSetup = systemSetup; + for (Task t : systemSetup.getTasks()) { + responseTimes.put(t, calculateResponseTime(t)); + } + + for (Entry taskResponsePair : responseTimes.entrySet()) { + if (taskResponsePair.getKey().getDeadline() < taskResponsePair.getValue().longValue()) { + return false; + } + } + + return true; + } + + @Override + public String getName() { + return "SchmidMottok"; + } + + private long calculateResponseTime(Task task) { + long minimumWcet = getMinimumWcet(task); + long responseTime = minimumWcet; + long previousResponseTime = 0; + + + do { + previousResponseTime = responseTime; + long interference = 0; + + for (Task t : systemSetup.getTasks()) { + if (t.getPeriod() < task.getPeriod()) { + interference += Math.min(getTaskInterference(t, responseTime), + responseTime - minimumWcet + 1); + } + } + + double taskInterference = (double) interference / 4; + double selfInterference = getSelfInterference(task); + + long totalInterference = (long) Math.floor(taskInterference + selfInterference); + + responseTime = minimumWcet + totalInterference; + } while (previousResponseTime != responseTime); + + + return responseTime; + } + + + private double getSelfInterference(Task task) { + double interference = 0; + + for (Segment s : task.getSegments()) { + long processingUnits = s.getNumberOfJobs() > 4 ? 4 : s.getNumberOfJobs(); + interference += + (double) (s.getNumberOfTasklets() - 1) * s.getTaskletWcet() / processingUnits; + } + + return interference; + } + + private long getTaskInterference(Task task, long interval) { + long responseTime = responseTimes.get(task); + long minWcet = getMinimumWcet(task); + long period = task.getPeriod(); + long interference = + (LongMath.divide(interval + responseTime - minWcet, period, RoundingMode.FLOOR) + 1) + * getMaximumWcet(task); + + return interference; + } + + private long getMinimumWcet(Task task) { + long minWcet = 0; + for (Segment s : task.getSegments()) { + minWcet += s.getTaskletWcet(); + } + + return minWcet; + } + + private long getMaximumWcet(Task task) { + long maxWcet = 0; + for (Segment s : task.getSegments()) { + maxWcet += s.getTaskletWcet() * s.getNumberOfTasklets(); + } + + return maxWcet; + } +} diff --git b/src/main/java/mvd/jester/tests/TestEnvironment.java a/src/main/java/mvd/jester/tests/TestEnvironment.java new file mode 100644 index 0000000..8354b01 --- /dev/null +++ a/src/main/java/mvd/jester/tests/TestEnvironment.java @@ -0,0 +1,75 @@ +package mvd.jester.tests; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import mvd.jester.model.SortedTaskSet; +import mvd.jester.model.SystemSetup; + +/** + * TestEnvironment + */ +public class TestEnvironment { + + private final long numberOfTaskSets; + private final long numberOfProcessors; + private final Set> abstractTests; + + public TestEnvironment(long numberOfTaskSets, long numberOfProcessors) { + this.numberOfTaskSets = numberOfTaskSets; + this.numberOfProcessors = numberOfProcessors; + abstractTests = new HashSet<>(); + } + + public TestEnvironment registerTestInterface(Class abstractTest) { + abstractTests.add(abstractTest); + + return this; + } + + public void runTests() { + Map testCases = new HashMap<>(); + + for (Class t : abstractTests) { + try { + testCases.put(t.newInstance(), (long) 0); + } catch (Exception e) { + System.out.println("Ahhh SHIT!"); + } + } + + + long checkedTasksets = 0; + + while (checkedTasksets < 4000) { + SystemSetup.Generator generator = new SystemSetup.Generator(); + SystemSetup systemSetup = generator.build(); + + double utilization = systemSetup.getUtilization(); + + while (utilization <= systemSetup.getNumberOfProcessors() + && checkedTasksets < numberOfTaskSets) { + checkedTasksets++; + + for (AbstractTest t : testCases.keySet()) { + if (t.runSchedulabilityCheck(systemSetup)) { + testCases.computeIfPresent(t, (k, v) -> v + 1); + } + } + generator.addTask(systemSetup); + + utilization = systemSetup.getUtilization(); + } + } + + + for (AbstractTest t : testCases.keySet()) { + System.out.println("Testcase " + t.getName() + " scheduled " + testCases.get(t) + " of " + + numberOfTaskSets + " tasks!"); + } + + + } +} diff --git b/src/main/java/mvd/jester/tests/TestInterface.java a/src/main/java/mvd/jester/tests/TestInterface.java new file mode 100644 index 0000000..b92f13d --- /dev/null +++ a/src/main/java/mvd/jester/tests/TestInterface.java @@ -0,0 +1,14 @@ +package mvd.jester.tests; + +import mvd.jester.model.SystemSetup; + +/** + * TestInterface + */ +public interface TestInterface { + + public boolean runSchedulabilityCheck(SystemSetup systemSetup); + + public String getName(); + +} diff --git b/src/main/java/mvd/jester/utils/TimeUnit.java a/src/main/java/mvd/jester/utils/TimeUnit.java new file mode 100644 index 0000000..8f70a45 --- /dev/null +++ a/src/main/java/mvd/jester/utils/TimeUnit.java @@ -0,0 +1,40 @@ +package mvd.jester.utils; + +public class TimeUnit { + public static long Seconds(long seconds) { + return seconds*1_000_000_000; + } + + public static long Seconds(double seconds) { + return Math.round(seconds*1_000_000_000); + } + + public static long Milliseconds(long milliseconds) { + return milliseconds*1_000_000; + } + + public static long Milliseconds(double milliseconds) { + return Math.round(milliseconds*1_000_000); + } + + public static long Microseconds(long microseconds) { + return microseconds*1_000; + } + + public static long Microseconds(double microseconds) { + return Math.round(microseconds*1_000); + } + + public static long Nanoseconds(long nanoseconds) { + return nanoseconds; + } + + public static long Nanoseconds(double nanoseconds) { + return Math.round(nanoseconds); + } + + public static long RoundTo(long value, long round) { + return value / round * round; + } +} + diff --git b/src/test/java/mvd/jester/model/TaskSetTest.java a/src/test/java/mvd/jester/model/TaskSetTest.java new file mode 100644 index 0000000..4f23b0c --- /dev/null +++ a/src/test/java/mvd/jester/model/TaskSetTest.java @@ -0,0 +1,41 @@ +package mvd.jester.model; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +public class TaskSetTest { + + private static final int NUMBER_OF_SETS = 1000; + + @Test + @DisplayName("Check if randomly generated task parameters lie within the specified ranges.") + public void testRandomTaskSetGeneration() { + + for (int i = 0; i < NUMBER_OF_SETS; ++i) { + SystemSetup taskSet = new SystemSetup.Generator().build(); + + for (Task t : taskSet.getTasks()) { + assertTrue(t.getPeriod() >= 100); + assertTrue(t.getPeriod() <= 1000); + assertTrue(t.getDeadline() == t.getPeriod()); + + long maxJobWcet = t.getPeriod() / t.getSegments().size(); + + for (Segment s : t.getSegments()) { + assertTrue(s.getJobWcet() * s.getNumberOfJobs() == s.getTaskletWcet() + * s.getNumberOfTasklets()); + assertTrue(s.getJobWcet() >= 1); + assertTrue(s.getJobWcet() <= maxJobWcet); + assertTrue(s.getNumberOfJobs() >= 1); + assertTrue(s.getNumberOfJobs() <= 6); + } + + assertTrue(t.getSegments().size() >= 1); + assertTrue(t.getSegments().size() <= 5); + } + } + + } +}