WorkflowSim HEFT 代码注解

/**
 * 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; 
    // 任务Task在某vm上的计算代价时间
    
    private Map<Task, Map<Task, Double>> transferCosts;
    // 任务与任务间的传输代价时间
    
    private Map<Task, Double> rank;
    // 任务的“rank”
    
    private Map<CondorVM, List<Event>> schedules;
    // vm上面的调度事件(一个一个的时间片)
   
    private Map<Task, Double> earliestFinishTimes;
    // 任务的最早完成时间EFT
    
    private double averageBandwidth;
    // 平均带宽,此值可用于计算任务到任务的传输代价时间
    
    // vm上的一个调度事件(一个时间片)
    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);
            // o.rank比rank大时,返回值为1;等:0;小:-1.
        }
    }

    // 五大初始化
    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();
        // 平均带宽,此值可用于计算任务到任务的传输成本

        // 初始化schedule
        for (Object vmObject : getVmList()) {
            CondorVM vm = (CondorVM) vmObject;
            schedules.put(vm, new ArrayList<>());
        } 

        // Prioritization phase
        calculateComputationCosts();
        // 计算每个task在每台vm上面的运行时间
        
        calculateTransferCosts();
        // 计算有前后关系的任务i到j的传输成本
        
        calculateRanks();

        // Selection phase
        allocateTasks();
    }

    /**
     * Calculates the average available bandwidth among all VMs in Mbit/s
     *
     * @return Average available bandwidth in Mbit/s
     */
    // 计算所有vm的平均带宽,即vm.getBw()
    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.
     */
    // 计算每个task在每台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()) { //这台vm处理不了
                    costsVm.put(vm, Double.MAX_VALUE);// 赋值最大
                } else { //处理得了
                    costsVm.put(vm,
                            task.getCloudletTotalLength() / vm.getMips());
                    // 时间 = 总量/速度
                }
            }
            computationCosts.put(task, costsVm);
        }
		// 最终可得每个task在每台vm上面的运行时间(如不能完成,则为Double最大值)
		// 结果保存于computationCosts中
    }

    /**
     * Populates the transferCosts map with the time in seconds to transfer all
     * files from each parent to each child
     */
    // 计算有前后关系的任务i到j的传输成本
    private void calculateTransferCosts() {
        // Initializing the matrix
		// 初始化
		// 对每一个任务i,transferCosts.put(i, <j,0.0>(j=1..n));
        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()) {
            	// parent.getChildList() 可取parent任务的子任务列表
                transferCosts.get(parent).put(child,
                        calculateTransferCost(parent, child));
                // 注意后面的这个calculateTransferCost(parent, child)函数是不加s的Cost,在后文有定义。
            }
        }
    }

    /**
     * Accounts the time in seconds necessary to transfer all files described
     * between parent and child
     *
     * @param parent
     * @param child
     * @return Transfer cost in seconds
     */
	//计算传输成本,由parent到child仅一道直系
	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) {
			// 如果当前parentFile.getType()不符要求Parameters.FileType.OUTPUT,则不算,跳过
			if (parentFile.getType() != Parameters.FileType.OUTPUT) {
				continue;
			}

			for (FileItem childFile : childFiles) {
				// 如果childFile.getType()符合要求Parameters.FileType.INPUT 且父文件与子文件getName()相等
				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)) {
			// 递归结束条件之一:若已经计算,即rank已经存有task的值,则直接返回rank.get(task)
			return rank.get(task);
		}

		double averageComputationCost = 0.0;

		// 对此task在所有可被执行的vm上的各项时间(共computationCosts.get(task).size()项)求累加和
		for (Double cost : computationCosts.get(task).values()) {
			averageComputationCost += cost;
		}
		
		// 总和sum/总数computationCosts.get(task).size(),得到平均值
		averageComputationCost /= computationCosts.get(task).size();

		double max = 0.0;
		// 对当前的任务task,dfs处理其所有子任务child_i,求时间代价总和
		for (Task child : task.getChildList()) {
			double childCost = transferCosts.get(task).get(child) + calculateRank(child);
			// 此子任务child的时间代价 = 父子传输的cost + 子与子孙的cost(递归求和)			
			max = Math.max(max, childCost);
			// makespan是取各个子分支里最长的那一支的时间代价
		}

		rank.put(task, averageComputationCost + max);
		// rank_task = 此任务在各台可行vm上的平均时间代价+各个子分支里最长的那一支的时间代价

		return rank.get(task);
	}

    /**
     * Allocates all tasks to be scheduled in non-ascending order of schedule.
     */
	private void allocateTasks() {
		List<TaskRank> taskRank = new ArrayList<>();
		
		// 为便于排序,将上述rank里存储的结果“捯腾”进一个新的列表taskRank中。
		// 其中,第一键值是task,第二键值是时间代价。
		for (Task task : rank.keySet()) {
			taskRank.add(new TaskRank(task, rank.get(task)));
		}

		// Sorting in non-ascending order of rank
		// taskRank里的键值对,降序排序
		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; //待选vm,初始为空
		double earliestFinishTime = Double.MAX_VALUE; //最早完成时间EFT,初始为最大值
		double bestReadyTime = 0.0;
		double finishTime;

		for (Object vmObject : getVmList()) { // 遍历各个vmObject
			CondorVM vm = (CondorVM) vmObject; // 强制类型转换:vmObject->CondorVM,便于后续处理
			double minReadyTime = 0.0;

			for (Task parent : task.getParentList()) {
				// 就绪时间 初始化,等于 父任务的最早完成时间
				double readyTime = earliestFinishTimes.get(parent);
				
				// 如果当前任务task与其父任务parent不在同一台vm上面执行(vm.getId不相等)
				if (parent.getVmId() != vm.getId()) { 
					// 则当前任务task的就绪时间还要加上与父任务之间的传输时间
					readyTime += transferCosts.get(parent).get(task);
				}
				minReadyTime = Math.max(minReadyTime, readyTime);
				// 此task最终的最小等待时间,等于其各个前驱节点最小等待时间的最大值,因为要把前面的任务全部完成嘛。
			}

			// 计算任务task在虚拟机vm上的完成时间
			finishTime = findFinishTime(task, vm, minReadyTime, false);

			// 更新EFT,以及对应EFT的最好开始时间、待选vm
			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) {
		// occupySlot……是否抢占
		
		List<Event> sched = schedules.get(vm);
		// 获取这台vm对应的列表,是为List<Event> sched
		
		double computationCost = computationCosts.get(task).get(vm);
		// 此task在vm上的计算代价时间
		
		double start, finish;
		int pos;
		
		// 如果vm对应的列表空着(最简单的情况)
		if (sched.isEmpty()) {
			//如果是“抢占”式任务
			if (occupySlot) {
				// 那么直接“占”,起始时间是就绪时间,结束时间再加上计算代价时间
				sched.add(new Event(readyTime, readyTime + computationCost));
			}
			// 完成时间 = 就绪时间 + 计算代价开销
			return readyTime + computationCost;
		}

		// 如果vm对应的列表已经有了一个时间片段,或者说事件(比较简单的情况)
		if (sched.size() == 1) {
			// 当前任务的就绪时间,比vm上那个时间片的结束时间还要晚
			if (readyTime >= sched.get(0).finish) {
				pos = 1;
				start = readyTime; // 起始时间 就等于 就绪时间
			} else if (readyTime + computationCost <= sched.get(0).start) {
				// 当前任务的就绪时间 + 计算代价时间,比vm上那个时间片的起始时间还要早
				pos = 0;
				start = readyTime; // 起始时间 仍等于 就绪时间
			} else {
				// 当前任务从就绪时间开始,与vm上已有的时间片存在重叠
				pos = 1; // 执行“后来者靠后”的策略
				start = sched.get(0).finish; // 起始时间 等于 vm上那个原有时间片的结束时间
			}

			if (occupySlot) {
				// 若“抢占”
				sched.add(pos, new Event(start, start + computationCost));
			}
			return start + computationCost;
		}

		// Trivial case: Start after the latest task scheduled
        // 一般情况:在计划的最新任务之后启动
		
        // 起始时间 等于 max(就绪时间,vm上最末时间片的结束时间)
		start = Math.max(readyTime, sched.get(sched.size() - 1).finish);
		
		// 结束时间 等于 起始时间 + 计算代价时间
		finish = start + computationCost;
		
		// vm上原有的最末
		int i = sched.size() - 1;
		
		// vm上原有的倒数第二
		int j = sched.size() - 2;
		
		// 当前任务即将放入的位置
		pos = i + 1;

		// 循环,挨个往前尝试,看能否插入vm上原有的两个任务之间
		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;
				// 跳出while循环
				// 前一任务完成时,当前任务还未就绪,则不可能将当前任务放到前一任务之前,不必再继续循环了
			}
			
			// 前一任务的完成时间 + 当前任务的计算代价时间 不晚于 后一任务的起始时间
			// 在完成前一任务之后执行当前任务,可行
			// 注意这里没有比较previous.finish与readyTime的先与后
			// 但因为前面有break,所以previous.finish一定不小于readyTime
			// 即当前任务就绪时,前一任务还没有完成
			// 亦即,前一任务完成时,当前任务一定已经就绪了
			if (previous.finish + computationCost <= current.start) {
				// 如果前一任务的完成时间 + 计算代价开销 不晚于 后一任务的起始时间
				// 即,当前任务可以“插队”在前后两任务之间直接执行
				
				start = previous.finish;
				// 当前任务的起始时间 = 前一任务的完成时间;
				
				finish = previous.finish + computationCost;
				// 当前任务的完成时间 = 起始时间 + 计算代价开销 = 前一任务的完成时间 + 计算代价开销
				pos = i;
			}
			i--;
			j--;
		}

		if (readyTime + computationCost <= sched.get(0).start) {
			// 如果当前任务可以放在vm的最开始
			
			pos = 0;
			// 最开始的位置的标号是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;
	}
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
heft算法(Heterogeneous Earliest Finish Time,异构最早完成时间算法)是用于解决作业调度问题的一种启发式算法。 heft算法通过计算任务的最早完成时间来确定作业之间的调度顺序。在heft算法中,每个任务都会被分配到一个合适的计算节点执行。计算节点根据自身的计算能力以及已经分配的任务来决定是否接受新的任务。通过根据任务的通信开销和计算开销来计算任务的最早完成时间,heft算法可以找到一种合适的作业调度,从而最大程度地降低整体作业执行时间。 heft算法的主要步骤包括:首先,根据任务的计算开销和数据传输开销构建任务调度图;然后,根据任务的计算开销和数据传输开销计算每个任务的最早完成时间;接下来,根据任务的最早完成时间和计算节点的计算能力,确定每个任务的执行节点;最后,根据任务的执行节点,确定任务的执行顺序,得到最优的作业调度。 heft算法的优点是可以有效地减少作业的执行时间,提高作业的执行效率。它考虑了任务之间的数据传输开销和计算开销,能够找到最佳的作业调度。而且,heft算法是一种启发式算法,计算复杂度相对较低,非常适合应用于实际的作业调度问题中。 总之,heft算法是一种用于解决作业调度问题的启发式算法。通过计算任务的最早完成时间,并根据计算节点的能力和任务之间的通信开销,找到最优的作业调度,从而提高作业的执行效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值