/** * Copyright 2012-2013 University Of Southern California * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.workflowsim.planning; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.cloudbus.cloudsim.Consts; import org.cloudbus.cloudsim.Log; import org.workflowsim.CondorVM; import org.workflowsim.FileItem; import org.workflowsim.Task; import org.workflowsim.utils.Parameters; /** * The HEFT planning algorithm. * * @author Pedro Paulo Vezzá Campos * @date Oct 12, 2013 */ public class HEFTPlanningAlgorithm extends BasePlanningAlgorithm { private Map<Task, Map<CondorVM, Double>> computationCosts; private Map<Task, Map<Task, Double>> transferCosts; private Map<Task, Double> rank; private Map<CondorVM, List<Event>> schedules; private Map<Task, Double> earliestFinishTimes; private double averageBandwidth; private class Event { public double start; public double finish; public Event(double start, double finish) { this.start = start; this.finish = finish; } } private class TaskRank implements Comparable<TaskRank> { public Task task; public Double rank; public TaskRank(Task task, Double rank) { this.task = task; this.rank = rank; } @Override public int compareTo(TaskRank o) { return o.rank.compareTo(rank); } } public HEFTPlanningAlgorithm() { computationCosts = new HashMap<>(); transferCosts = new HashMap<>(); rank = new HashMap<>(); earliestFinishTimes = new HashMap<>(); schedules = new HashMap<>(); } /** * The main function */ @Override public void run() { Log.printLine("HEFT planner running with " + getTaskList().size() + " tasks."); averageBandwidth = calculateAverageBandwidth(); for (Object vmObject : getVmList()) { CondorVM vm = (CondorVM) vmObject; schedules.put(vm, new ArrayList<>()); } // Prioritization phase calculateComputationCosts(); calculateTransferCosts(); calculateRanks(); // Selection phase allocateTasks(); } /** * Calculates the average available bandwidth among all VMs in Mbit/s * * @return Average available bandwidth in Mbit/s */ private double calculateAverageBandwidth() { double avg = 0.0; for (Object vmObject : getVmList()) { CondorVM vm = (CondorVM) vmObject; avg += vm.getBw(); } return avg / getVmList().size(); } /** * Populates the computationCosts field with the time in seconds to compute * a task in a vm. */ private void calculateComputationCosts() { for (Task task : getTaskList()) { Map<CondorVM, Double> costsVm = new HashMap<>(); for (Object vmObject : getVmList()) { CondorVM vm = (CondorVM) vmObject; if (vm.getNumberOfPes() < task.getNumberOfPes()) { costsVm.put(vm, Double.MAX_VALUE); } else { costsVm.put(vm, task.getCloudletTotalLength() / vm.getMips()); } } computationCosts.put(task, costsVm); } } /** * Populates the transferCosts map with the time in seconds to transfer all * files from each parent to each child */ private void calculateTransferCosts() { // Initializing the matrix for (Task task1 : getTaskList()) { Map<Task, Double> taskTransferCosts = new HashMap<>(); for (Task task2 : getTaskList()) { taskTransferCosts.put(task2, 0.0); } transferCosts.put(task1, taskTransferCosts); } // Calculating the actual values for (Task parent : getTaskList()) { for (Task child : parent.getChildList()) { transferCosts.get(parent).put(child, calculateTransferCost(parent, child)); } } } /** * Accounts the time in seconds necessary to transfer all files described * between parent and child * * @param parent * @param child * @return Transfer cost in seconds */ private double calculateTransferCost(Task parent, Task child) { List<FileItem> parentFiles = parent.getFileList(); List<FileItem> childFiles = child.getFileList(); double acc = 0.0; for (FileItem parentFile : parentFiles) { if (parentFile.getType() != Parameters.FileType.OUTPUT) { continue; } for (FileItem childFile : childFiles) { if (childFile.getType() == Parameters.FileType.INPUT && childFile.getName().equals(parentFile.getName())) { acc += childFile.getSize(); break; } } } //file Size is in Bytes, acc in MB acc = acc / Consts.MILLION; // acc in MB, averageBandwidth in Mb/s return acc * 8 / averageBandwidth; } /** * Invokes calculateRank for each task to be scheduled */ private void calculateRanks() { for (Task task : getTaskList()) { calculateRank(task); } } /** * Populates rank.get(task) with the rank of task as defined in the HEFT * paper. * * @param task The task have the rank calculates * @return The rank */ private double calculateRank(Task task) { if (rank.containsKey(task)) { return rank.get(task); } double averageComputationCost = 0.0; for (Double cost : computationCosts.get(task).values()) { averageComputationCost += cost; } averageComputationCost /= computationCosts.get(task).size(); double max = 0.0; for (Task child : task.getChildList()) { double childCost = transferCosts.get(task).get(child) + calculateRank(child); max = Math.max(max, childCost); } rank.put(task, averageComputationCost + max); return rank.get(task); } /** * Allocates all tasks to be scheduled in non-ascending order of schedule. */ private void allocateTasks() { List<TaskRank> taskRank = new ArrayList<>(); for (Task task : rank.keySet()) { taskRank.add(new TaskRank(task, rank.get(task))); } // Sorting in non-ascending order of rank Collections.sort(taskRank); for (TaskRank rank : taskRank) { allocateTask(rank.task); } } /** * Schedules the task given in one of the VMs minimizing the earliest finish * time * * @param task The task to be scheduled * @pre All parent tasks are already scheduled */ private void allocateTask(Task task) { CondorVM chosenVM = null; double earliestFinishTime = Double.MAX_VALUE; double bestReadyTime = 0.0; double finishTime; for (Object vmObject : getVmList()) { CondorVM vm = (CondorVM) vmObject; double minReadyTime = 0.0; for (Task parent : task.getParentList()) { double readyTime = earliestFinishTimes.get(parent); if (parent.getVmId() != vm.getId()) { readyTime += transferCosts.get(parent).get(task); } minReadyTime = Math.max(minReadyTime, readyTime); } finishTime = findFinishTime(task, vm, minReadyTime, false); if (finishTime < earliestFinishTime) { bestReadyTime = minReadyTime; earliestFinishTime = finishTime; chosenVM = vm; } } findFinishTime(task, chosenVM, bestReadyTime, true); earliestFinishTimes.put(task, earliestFinishTime); task.setVmId(chosenVM.getId()); } /** * Finds the best time slot available to minimize the finish time of the * given task in the vm with the constraint of not scheduling it before * readyTime. If occupySlot is true, reserves the time slot in the schedule. * * @param task The task to have the time slot reserved * @param vm The vm that will execute the task * @param readyTime The first moment that the task is available to be * scheduled * @param occupySlot If true, reserves the time slot in the schedule. * @return The minimal finish time of the task in the vmn */ private double findFinishTime(Task task, CondorVM vm, double readyTime, boolean occupySlot) { List<Event> sched = schedules.get(vm); double computationCost = computationCosts.get(task).get(vm); double start, finish; int pos; if (sched.isEmpty()) { if (occupySlot) { sched.add(new Event(readyTime, readyTime + computationCost)); } return readyTime + computationCost; } if (sched.size() == 1) { if (readyTime >= sched.get(0).finish) { pos = 1; start = readyTime; } else if (readyTime + computationCost <= sched.get(0).start) { pos = 0; start = readyTime; } else { pos = 1; start = sched.get(0).finish; } if (occupySlot) { sched.add(pos, new Event(start, start + computationCost)); } return start + computationCost; } // Trivial case: Start after the latest task scheduled start = Math.max(readyTime, sched.get(sched.size() - 1).finish); finish = start + computationCost; int i = sched.size() - 1; int j = sched.size() - 2; pos = i + 1; while (j >= 0) { Event current = sched.get(i); Event previous = sched.get(j); if (readyTime > previous.finish) { if (readyTime + computationCost <= current.start) { start = readyTime; finish = readyTime + computationCost; } break; } if (previous.finish + computationCost <= current.start) { start = previous.finish; finish = previous.finish + computationCost; pos = i; } i--; j--; } if (readyTime + computationCost <= sched.get(0).start) { pos = 0; start = readyTime; if (occupySlot) { sched.add(pos, new Event(start, start + computationCost)); } return start + computationCost; } if (occupySlot) { sched.add(pos, new Event(start, finish)); } return finish; } }