一、Cloudsim 示例源码阅读笔记 以NonPowerAware示例

导师布置了阅读上面论文,关于面向云数据中心的虚拟机整合策略,其中用到了cloudsim作为仿真环境,在PlanetLab跟踪和Google集群跟踪进行了广泛的模拟实验。为了复现论文呢实验结果,需要对cloudsim代码进行详细研究,故发此文作笔记。
前言:仅聚焦主要和重点过程,并非完整流程,比如不含CloudSim初始化和broker对于云任务和虚机的提交等
1.1 基本内容

位置:org.cloudbus.cloudsim.examples.power.random中的NonPowerAware.java
功能:对异构非功耗感知数据中心的仿真,返回所有主机仿真过程中的最大功率
返回:所有主机仿真过程中的最大功率(最大能耗)
1.2 云任务/虚机/主机/功耗模型参数设置
位置:org.cloudbus.cloudsim.examples.power中的Constants.java
用户数量(numUser) = 1
数据中心数量 = 1
云任务时长:2500天
public final static double SCHEDULING_INTERVAL = 300;
public final static double SIMULATION_LIMIT = 24 * 60 * 60;
public final static int CLOUDLET_LENGTH = 2500 * (int) SIMULATION_LIMIT;
public final static int CLOUDLET_PES = 1;
4种类型虚拟机参数设置

2种异构类型物理机参数设置
/*
* Host types:
* HP ProLiant ML110 G4 (1 x [Xeon 3040 1860 MHz, 2 cores], 4GB)
* HP ProLiant ML110 G5 (1 x [Xeon 3075 2660 MHz, 2 cores], 4GB)
* We increase the memory size to enable over-subscription (x4)
*/
public final static int HOST_TYPES = 2;
public final static int[] HOST_MIPS = { 1860, 2660 };
public final static int[] HOST_PES = { 2, 2 };
public final static int[] HOST_RAM = { 4096, 4096 };
public final static int HOST_BW = 1000000; // 1 Gbit/s
public final static int HOST_STORAGE = 1000000; // 1 GB
2种异构物理机(惠普G4和惠普G5)的功耗模型
public final static PowerModel[] HOST_POWER = {
new PowerModelSpecPowerHpProLiantMl110G4Xeon3040(),
new PowerModelSpecPowerHpProLiantMl110G5Xeon3075()
};
在CloudSim中的仿真模型,实际编程是传入CPU利用率∈[0,1],归一化到[0,10]整数以表征[0%, 10%, 20%,…,90%,100%],通过整数索引来获取对应的能耗值(单位W),如该型号主机10%资源利用率对应能耗为89.4w
public class PowerModelSpecPowerHpProLiantMl110G4Xeon3040 extends PowerModelSpecPower {
/**
* The power consumption according to the utilization percentage.
* @see #getPowerData(int)
*/
private final double[] power = { 86, 89.4, 92.6, 96, 99.5, 102, 106, 108, 112, 114, 117 };
@Override
protected double getPowerData(int index) {
return power[index];
}
}
不同主机厂商功耗模型网址:http://www.spec.org/power_ssj2008/results/power_ssj2008.html

HP ProLiant ML110
G4型号主机为例,以说明空载状态下的主机都有一定能耗损失
HP ProLiant ML110
G5型号主机为例
因此,对于空载状态下高能耗的物理机,较好的虚机的放置策略/整合策略可节能更多。
1.3 初始化云任务(CloudletList)—如何载入自定义真实数据集中的CPU利用率?

进入到PlanetLabHelper.createCloudletListPlanetLab() 方法中,可见其对每个云任务中的{CPU利用率,RAM, Bw}设置方式

具体实现过程如下
- 定义1个data数组用来存储每个时刻的CPU利用率,1天按5分钟分割,则总长需存储288个时刻的CPU利用率
- 从Trace文件中逐个读取每个时刻的CPU利用率并存入data数组中
public UtilizationModelPlanetLabInMemory(String inputPath, double schedulingInterval)
throws NumberFormatException,
IOException {
//288*5 = 60*24 即表示一天的CPU利用率,每5min一个样本
data = new double[289];
setSchedulingInterval(schedulingInterval);
BufferedReader input = new BufferedReader(new FileReader(inputPath));
int n = data.length;
for (int i = 0; i < n - 1; i++) {
data[i] = Integer.parseInt(input.readLine()) / 100.0;
}
data[n - 1] = data[n - 2];
input.close();
}
根据不同时刻,作为索引可直接从data中获取对应CPU利用率,此处仅需简单处理下时间不能整除时的情况
@Override
public double getUtilization(double time) {
if (time % getSchedulingInterval() == 0) {
return data[(int) time / (int) getSchedulingInterval()];
}
int time1 = (int) Math.floor(time / getSchedulingInterval());
int time2 = (int) Math.ceil(time / getSchedulingInterval());
double utilization1 = data[time1];
double utilization2 = data[time2];
double delta = (utilization2 - utilization1) / ((time2 - time1) * getSchedulingInterval());
return utilization1 + delta * (time - time1 * getSchedulingInterval());
}
1.4 初始化虚拟机(vmList)
回到NonPowerAware.java
本例共创建1052个虚拟机
List<Vm> vmList = Helper.createVmList(brokerId, cloudletList.size());
虚机中对于云任务所需CPU资源的调度策略默认为时间共享,从该方法继承自CloudletSchedulerTimeShared可见
public static List<Vm> createVmList(int brokerId, int vmsNumber) {
List<Vm> vms = new ArrayList<>();
for (int i = 0; i < vmsNumber; i++) {
int vmType = i / (int) Math.ceil((double) vmsNumber / Constants.VM_TYPES);
vms.add(new PowerVm(
i,
brokerId,
Constants.VM_MIPS[vmType],
Constants.VM_PES[vmType],
Constants.VM_RAM[vmType],
Constants.VM_BW,
Constants.VM_SIZE,
1,
"Xen",
new CloudletSchedulerDynamicWorkload(Constants.VM_MIPS[vmType], Constants.VM_PES[vmType]),
Constants.SCHEDULING_INTERVAL));
}
return vms;
}
public class CloudletSchedulerDynamicWorkload extends CloudletSchedulerTimeShared
1.5 初始化物理机(hostList)——如何自定义不同型号物理机配置?
同上,此处物理机的CPU调度策略依然默认时间共享
List<PowerHost> hostList = Helper.createHostList(PlanetLabConstants.NUMBER_OF_HOSTS);
进入到Helper.createHostList()方法中,可以发现此处for循环中的hostList.add方法可以通过自定义参数和函数以自定义不同型号物理机配置
比如案例中的不同主机硬盘存储均为同一定值,可以依照RamProvisionerSimple自定义StorageProvisionerSimple实现不同主机拥有不同硬盘存储
/**
* Creates the host list.
*
* @param hostsNumber the hosts number
*
* @return the list< power host>
*/
public static List<PowerHost> createHostList(int hostsNumber) {
List<PowerHost> hostList = new ArrayList<>();
for (int i = 0; i < hostsNumber; i++) {
int hostType = i % Constants.HOST_TYPES;
List<Pe> peList = new ArrayList<>();
for (int j = 0; j < Constants.HOST_PES[hostType]; j++) { //每个host上有2核CPU处理器
peList.add(new Pe(j, new PeProvisionerSimple(Constants.HOST_MIPS[hostType])));
}
hostList.add(new PowerHost(
i,
new RamProvisionerSimple(Constants.HOST_RAM[hostType]), //为每台物理机设置RAM
new BwProvisionerSimple(Constants.HOST_BW), //为每台物理机设置bandwidth
Constants.HOST_STORAGE, //为每台物理机设置硬盘存储
peList, //物理机多核设置
new VmSchedulerTimeSharedOverSubscription(peList), //时间共享
Constants.HOST_POWER[hostType])); //设置物理机功耗模型
}
return hostList;
}
1.6 初始化数据中心(datacenter)——在哪修改虚拟机分配策略?(默认First Fit)
PowerDatacenterNonPowerAware datacenter = (PowerDatacenterNonPowerAware) Helper.createDatacenter(
"Datacenter", //数据中心name
PowerDatacenterNonPowerAware.class, //数据中心类:此处为非能耗感知数据中心
hostList,
new VmAllocationPolicySimple(hostList)); //虚拟机分配策略
接下来,我们看看传入的虚拟机分配策略是什么?这里能耗虚机分配策略继承原始PowerVmAllocationPolicyAbstract
public class PowerVmAllocationPolicySimple extends PowerVmAllocationPolicyAbstract {...}
继续进入PowerVmAllocationPolicyAbstract,可见其用First Fit策略进行虚拟机到主机的映射,因此想要修改或对比其他如Best Fit策略,则可以 继承 PowerVmAllocationPolicyAbstract类, 并修改findHostForVm()中的算法即可
/**
* Finds the first host that has enough resources to host a given VM.
*
* @param vm the vm to find a host for it
* @return the first host found that can host the VM
*/
public PowerHost findHostForVm(Vm vm) {
for (PowerHost host : this.<PowerHost> getHostList()) {
if (host.isSuitableForVm(vm)) {
return host;
}
}
return null;
}
1.7 仿真结果

进入printResults可发现,结束后从当前数据中心状态获取以下等指标
- **能耗(kw/h)**——依据数据中心不同异构主机的功耗模型累计得到
- 虚机迁移数
- SLAV服务违反率
- 开机激活态主机数
1.8 非功耗感知和功耗感知有啥区别?
| 功耗计算方式 | 数据中心类别 | |
|---|---|---|
| 非功耗感知 | 使用该物理机CPU利用率为100%下的功耗作为实时功耗 |
PowerDatacenterNonPowerAware |
| 功耗感知 | 使用该物理机当前CPU利用率下的功耗作为实时功耗 |
PowerDatacenter |
1.8.1 非功耗感知数据中心(PowerDatacenterNonPowerAware)
我们进入PowerDatacenterNonPowerAware.java类中,这里它继承了功耗感知数据中心并重写了相关方法
public class PowerDatacenterNonPowerAware extends PowerDatacenter {...}
/*省略了部分代码*/
//当前数据中心最大功耗
double timeframePower = 0.0;
// 统计每台物理机最大功耗
for (PowerHost host : this.<PowerHost> getHostList()) {
double hostPower = 0.0;
//用主机最大功耗代替主机功耗
hostPower = host.getMaxPower() * timeDiff;
timeframePower += hostPower;
setPower(getPower() + timeframePower);
这里的具体实现,即传入100%的CPU利用率,获取最大功耗
default double getMaxPower() {
double power = 0;
try {
power = getPowerModel().getPower(1);
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
return power;
}
完整实现
public abstract class PowerModelSpecPower implements PowerModel {
@Override
public double getPower(double utilization) throws IllegalArgumentException {
if (utilization < 0 || utilization > 1) {
throw new IllegalArgumentException("Utilization value must be between 0 and 1");
}
// if CPU利用率能整除10%,则直接代入索引获取对应功耗
if (utilization % 0.1 == 0) {
return getPowerData((int) (utilization * 10));
}
// then CPU利用率不能整除10%,则按比例插值计算
int utilization1 = (int) Math.floor(utilization * 10);
int utilization2 = (int) Math.ceil(utilization * 10);
double power1 = getPowerData(utilization1);
double power2 = getPowerData(utilization2);
double delta = (power2 - power1) / 10;
return power1 + delta * (utilization - (double) utilization1 / 10) * 100;
}
protected abstract double getPowerData(int index);
}
1.8.2 功耗感知数据中心(PowerDatacenter)
我们进入PowerDatacenter.java类中,在updateCloudetProcessingWithoutSchedulingFutureEventsForce()我们最终找到,获取数据中心能耗的流程。
protected double updateCloudetProcessingWithoutSchedulingFutureEventsForce() {
//省略了部分日志记录和虚拟机进程更新代码
double currentTime = CloudSim.clock();
double minTime = Double.MAX_VALUE;
double timeDiff = currentTime - getLastProcessTime();
// 初始化数据中心能耗=0
double timeFrameDatacenterEnergy = 0.0;
// 距离上次处理已经过去了一段时间
if (timeDiff > 0) {
//统计每个物理机的能耗
for (PowerHost host : this.<PowerHost> getHostList()) {
// 5min前的物理机cpu利用率
double previousUtilizationOfCpu = host.getPreviousUtilizationOfCpu();
// 当前时刻物理机的CPU利用率
double utilizationOfCpu = host.getUtilizationOfCpu();
// 物理机能耗(通过线性插值的方式获取:两点中间取中间点即可)
double timeFrameHostEnergy = host.getEnergyLinearInterpolation(
previousUtilizationOfCpu,
utilizationOfCpu,
timeDiff); //timeDiff = currentTime = getLastProcessTime()
timeFrameDatacenterEnergy += timeFrameHostEnergy;
}
}
//更新能耗值
setPower(getPower() + timeFrameDatacenterEnergy);
return minTime;
}
线性插值的方式很简单,两点中取中间点即可
public double getEnergyLinearInterpolation(double fromUtilization, double toUtilization, double time) {
if (fromUtilization == 0) {
return 0;
}
double fromPower = getPower(fromUtilization);
double toPower = getPower(toUtilization);
return (fromPower + (toPower - fromPower) / 2) * time;
}
此处的getPower(利用率)即是使用该物理机功耗模型,输入利用率,获取对应的能耗值得到

1.9 载入PlanetLab 2011/3/3一天的CPU利用率数据进行实验
1.9.1 数据特征
2011/3/3当天有约1052个不同虚拟机CPU利用率数据文件,每个数据文件表示对应型号物理机当天的CPU利用率变换,采样频率为5min一次,1天共288个样本

1.9.2 具体修改
String inputFolder = NonPowerAware.class.getClassLoader().getResource("workload/planetlab/20110303").getPath();
关于载入真实数据的方法,在1.3 初始化云任务(CloudletList)—如何载入自定义真实数据集中的CPU利用率?中介绍过,此处不予赘述。
只需修改数据中心类别即可切换以上两种数据中心的不同功耗结果

以下给出基于功耗感知和非功耗感知在PlanetLab数据集同一天同样参数设置下的结果,在没有任何策略优化的情况下,功耗感知对功耗的实际计算更为贴切


| PlanetLab数据 | 主机数 | VM数 | 云任务数 | VM放置策略 | 虚机调度策略 | 主机调度策略 | 关机主机数 | 耗能(kw/h) |
|---|---|---|---|---|---|---|---|---|
| 功耗感知 | 800 | 1052 | 1052 | First Fit | 时间共享 | 时间共享 | 342 | 1086.59 |
| 非功耗感知 | 800 | 1052 | 1052 | First Fit | 时间共享 | 时间共享 | 457 | 2410.8 |