基于CloudSim Plus的计算卸载仿真设计
基于CloudSim Plus的计算卸载仿真设计
1. 前提介绍
仿真框架的实现,主要依托于仿真实体、以及仿真事件,简单介绍如下
1.1 仿真实体
-
继承
CloudSimEntity
类(推荐
)或者实现SimEntity
接口(不建议
)public class ExampleEntity extends CloudSimEntity {public ExampleEntity(Simulation simulation) {super(simulation);}@Overrideprotected void startInternal() {// 实体开始启动}@Overridepublic void processEvent(SimEvent evt) {// 接收仿真事件} }
-
该实体将交由
CloudSim
进行管理 -
各个实体之间因此也可以通信(通过仿真事件)
1.2 仿真事件
-
发送仿真事件
可以使用
schedule()
和scheduleNow()
来发送仿真事件schedule(final SimEntity dest, final double delay, final CloudSimTag tag, final Object data) scheduleNow(final SimEntity dest, final CloudSimTag tag, final Object data)
例如:实体启动时给自身发送事件
@Override protected void startInternal() {schedule(this, 1, CloudSimTag.ENTITY_UPDATE); }
主要包括:事件的接收实体,事件的到达时间,事件的标签
例如:
this
表示事件发送给该实体自身,1
表示事件一秒钟后到达,ENTITY_UPDATE
表示事件的标签 -
接收仿真事件
@Override public void processEvent(SimEvent evt) {CloudSimTag tag = evt.getTag();if (tag == CloudSimTag.ENTITY_UPDATE){// Do Something(在这里可以执行更新状态的逻辑)// 继续发送仿真事件,从而使得仿真实体每秒钟更新自身状态schedule(this, 1, CloudSimTag.ENTITY_UPDATE);} }
2. 基础设施层
2.1 边缘设备
-
实现方式:继承仿真实体类,然后给自身发送仿真事件,实现自身状态的更新(如:位置,能耗,利用率等)
public class DeviceEntity extends CloudSimEntity {public DeviceEntity(Simulation simulation) {super(simulation);}@Overrideprotected void startInternal() {schedule(this, 1, CloudSimTag.ENTITY_UPDATE);}@Overridepublic void processEvent(SimEvent evt) {CloudSimTag tag = evt.getTag();if (tag == CloudSimTag.ENTITY_UPDATE){updateStatus();schedule(this, 1, CloudSimTag.ENTITY_UPDATE);}}private void updateStatus() {// 更新能耗、利用率、节点位置、无线接入点切换等(根据需求自定义)} }
-
自定义的其他组件同理
3. 其他组件
3.1 任务调度算法接口
- 为任务执行计算卸载决策
- 计算节点内进行任务与虚拟机匹配调度
- 任务结果返回事件(便于扩展强化学习算法)
public abstract class AbstractAlgorithm extends DatacenterBrokerSimple {protected SimManager simulationManager;public SimManager getSimulationManager() {return simulationManager;}public AbstractAlgorithm(SimManager simulation) {super(simulation.getSimulation());this.simulationManager = simulation;}/* 选择任务处理方式:本地处理、边缘处理、云端处理* @param cloudlet 待处理的任务* @return 为任务选择的计算节点*/public abstract AbstractNode makeDecision(Cloudlet cloudlet);/* 任务在计算节点内的调度策略 @param cloudlet 待调度的任务* @return Vm 为任务选择的虚拟机*/public abstract Vm scheduleTask(Cloudlet cloudlet);/* 任务返回: 例如使用强化学习算法计算奖励值 @param cloudlet 执行完成的任务*/public abstract void resultReturn(Cloudlet cloudlet);@Overrideprotected Vm defaultVmMapper(Cloudlet cloudlet) {if (cloudlet.isBoundToVm()) {return cloudlet.getVm();}Vm mappedVm = Vm.NULL;if (!getVmCreatedList().isEmpty()){mappedVm = scheduleTask(cloudlet);}if (mappedVm == Vm.NULL) {LOGGER.warn("{}: {}: {} (PEs: {}) couldn't be mapped to any suitable VM.",getSimulation().clockStr(), getName(), cloudlet, cloudlet.getNumberOfPes());} else {LOGGER.trace("{}: {}: {} (PEs: {}) mapped to {} (available PEs: {}, tot PEs: {})",getSimulation().clockStr(), getName(), cloudlet, cloudlet.getNumberOfPes(), mappedVm,mappedVm.getExpectedFreePesNumber(), mappedVm.getFreePesNumber());}return mappedVm;}@Overridepublic void processEvent(final SimEvent ev) {super.processEvent(ev);if (ev.getTag() == CloudSimTag.CLOUDLET_RETURN) {Cloudlet cloudlet = (Cloudlet) ev.getData();resultReturn(cloudlet);}} }
3.2 能耗模型
-
1.主机功耗数据来源:SPEC官网
-
例如型号为
AcerR380F2
的主机功耗数据,其中11个数据分别对于主机利用率从0, 0.1, 0.2, ..., 1
的功耗double[] AcerR380F2 = { 63.7, 78.0, 87.0, 96.5, 106, 116, 135, 158, 188, 221, 252};
-
2.为了计算功耗,可以基于主机功耗数据,拟合利用率-功耗函数,从而根据利用率来计算能耗
new PowerModelHostFunc(x -> 100.7 + 103.6 * x - 8.7 * x * x);
PowerModelHostFunc
介绍如下:public class PowerModelHostFunc extends PowerModelHost {/* 功率计算函数*/Function<Double,Double> function;public PowerModelHostFunc(Function<Double, Double> function) {this.function = function;}@Overridepublic double getPower(double utilization) throws IllegalArgumentException {if (utilization < 0 || utilization > 1) {throw new IllegalArgumentException("utilizationFraction has to be between [0 and 1]");}return function.apply(utilization);}@Overridepublic PowerMeasurement getPowerMeasurement() {final double utilization = getHost().getCpuMipsUtilization() / getHost().getTotalMipsCapacity();Double dynamicPower = function.apply(utilization);return new PowerMeasurement(0, dynamicPower);} }
-
2.或者,直接基于主机利用率功耗数据,来计算能耗
new PowerModelHostSpec(Arrays.stream(AcerR380F2).boxed().collect(Collectors.toList()));
PowerModelHostSpec
介绍如下:/* 原作者代码存在问题: 一般 SPEC 官网提供的功率数据为11个,即为 0,10,20,...,100%的利用率功耗数据;* 而原作者考虑不周到,假设有10个数据,从而造成数组越界.* 更正:* 修改获得当前功率的代码从 powerSpec.size() 改成 (powerSpec.size()-1)* 例如:当前功率为 98%, 则 Math.round(0.98 * 10) = 10, 即取功率为 100% 的利用率数据*/ public class PowerModelHostSpec extends PowerModelHost {public static final int MIN_POWER_CONSUMPTION_DATA_SIZE = 2;private final List<Double> powerSpec;public PowerModelHostSpec(final List<Double> powerSpec) {super();Objects.requireNonNull(powerSpec, "powerSpec cannot be null");if (powerSpec.size() >= MIN_POWER_CONSUMPTION_DATA_SIZE) {this.powerSpec = powerSpec;return;}final String msg = String.format("powerSpec has to contain at least %d elements (representing utilization at 0%% and 100%% load, respectively)", MIN_POWER_CONSUMPTION_DATA_SIZE);throw new IllegalArgumentException(msg);}@Overridepublic PowerMeasurement getPowerMeasurement() {final double utilizationFraction = getHost().getCpuMipsUtilization() / getHost().getTotalMipsCapacity();final int utilizationIndex = (int) Math.round(utilizationFraction * (powerSpec.size()-1));final double powerUsage = powerSpec.get(utilizationIndex);return new PowerMeasurement(powerSpec.get(0), powerUsage - powerSpec.get(0));}@Overridepublic double getPower(final double utilizationFraction) throws IllegalArgumentException {final int utilizationIndex = (int) Math.round(utilizationFraction * (powerSpec.size()-1));return powerSpec.get(utilizationIndex);} }
仿真实验测试
1. 本地处理算法
1.1 实验设定
- 任务只在本地处理,不进行卸载处理
- 假设任务执行时,计算节点的功耗保持不变。因此,
任务消耗的能源
=处理时间
*当前功耗
- 任务处理延迟包括:
排队延迟
+执行延迟
1.2 仿真实验结果
-
计算节点利用率
-
计算节点能源消耗
-
仿真结果数据
{"algorithmName" : "LocalAlgorithm","taskSuccessRate" : 0.770289898928766,"avgTaskTotalDelay" : 1.732609574535639,"avgTaskTotalEnergy" : 26.7213677578472,"avgCloudUtilization" : 0.0,"avgEdgeUtilization" : 0.0,"avgDeviceUtilization" : 29.972115384615385,"avgCloudEnergyConsume" : 22776.0,"avgEdgeEnergyConsume" : 35692.8,"avgTaskNetworkDelay" : 0.0,"cloudTaskExecuted" : 0.0,"cloudTaskSuccessExecuted" : 0.0,"edgeTaskExecuted" : 0.0,"edgeTaskSuccessExecuted" : 0.0,"deviceTaskExecuted" : 66092.0,"deviceTaskSuccessExecuted" : 50910.0 }
1.2 随机算法
-
计算节点平均利用率
-
计算节点能源消耗
-
仿真结果数据
{"algorithmName" : "RandomAlgorithm","taskSuccessRate" : 0.920377786173026,"avgTaskTotalDelay" : 1.2247280541003729,"avgTaskTotalEnergy" : 18.352311136392565,"avgCloudUtilization" : 38.35336538461539,"avgEdgeUtilization" : 35.13461538461538,"avgDeviceUtilization" : 9.517307692307694,"avgCloudEnergyConsume" : 95546.2,"avgEdgeEnergyConsume" : 67100.36,"avgDeviceEnergyConsume" : 12109.257999999996,"cloudTaskExecuted" : 22044.0,"cloudTaskSuccessExecuted" : 21781.0,"edgeTaskExecuted" : 21924.0,"edgeTaskSuccessExecuted" : 20995.0,"deviceTaskExecuted" : 22207.0,"deviceTaskSuccessExecuted" : 18130.0 }
1.3 贪心算法
-
计算节点平均利用率
-
计算节点能源消耗
-
仿真结果数据
{"algorithmName" : "GreedyAlgorithm","taskSuccessRate" : 0.9416770318235598,"avgTaskTotalDelay" : 1.2437790398350788,"avgTaskTotalEnergy" : 17.790207236463743,"avgCloudUtilization" : 28.44951923076923,"avgEdgeUtilization" : 30.355769230769226,"avgDeviceUtilization" : 16.62211538461539,"avgCloudEnergyConsume" : 72169.3,"avgEdgeEnergyConsume" : 63736.02,"avgDeviceEnergyConsume" : 15318.622999999994,"cloudTaskExecuted" : 2874.0,"cloudTaskSuccessExecuted" : 2397.0,"edgeTaskExecuted" : 6510.0,"edgeTaskSuccessExecuted" : 5667.0,"deviceTaskExecuted" : 56542.0,"deviceTaskSuccessExecuted" : 54017.0 }