2018年的华为软件精英挑战赛题目简介:给出华为云虚拟机过去的租借数量历史数据,用以训练模型并预测下一个时间段里的虚拟机租借数量,然后把这些预测得到的虚拟机装填进一定规格的物理机中,即分为预测和装填两个部分。
总结一下装填部分使用的模拟退火算法:
算法原理
装填的基础算法是FF(首次适应算法),而虚拟机的序列会影响FF算法的装填效果。比较明显的是FFD把序列降序之后再使用FF算法往往效果更好。而模拟退火算法目的就是获得一个较好的序列。
模拟退火的基本原理就是设置一个初始温度、一个最低温度,一个降温速率。例如初始温度取100,最低温度取1,降温速率取0.9 。则温度迭代从100开始,每次迭代都使当前温度变为0.9T,直至到达1为止。
每次迭代都随机交换虚拟机序列中的任意两个元素,然后用这个序列进行FF装填,得到一个评判分数J_cur,而设当前记录的最优分数为J_min(评判分数=目前使用物理机数-1+最后一个物理机的资源占有率,因此分数越低越好)。设dE=J_cur-J_min,
如果dE<=0,即当前分数更小,装填效果更好,则接受这个序列
如果dE>0,即当前分数更大,装填效果更差,则按一定几率接受这个序列
就是说温度越低,接受这个不合格的序列的可能性越低。这样的目的是让迭代前期接受更多不合格序列以期脱离局部最优,而迭代后期接受更少不合格序列以得到最优方案。
每次迭代都设一个0到1之间的随机数rand,用以跟P对比,如果P>rand,则接受这个方案。
迭代初期,由于T比较大,P趋近于1,因此很大概率会接收这个不合格方案,而迭代后期T比较小,P趋近于0,因此很大概率不接受这个方案。
如下图,蓝线为P、橙线为rand、灰线为P-rand。
可见随着迭代次数的增加,灰线会慢慢的变得小于0
可见P一直都很大,这样无论迭代多少次,都会一律接受这些不合格的方案,也就相当于随机序列了。原因是dE这个数值太小了,无论T怎么变化都没有用,所以我加了一个参数进去:
实际应用
单独摘出装填部分,则需要一个原始的虚拟机序列和一些基本参数。我都设置好了:
15种虚拟机规格为:
flavor1 1 1024
flavor2 1 2048
flavor3 1 4096
flavor4 2 2048
flavor5 2 4096
flavor6 2 8192
flavor7 4 4096
flavor8 4 8192
flavor9 4 16384
flavor10 8 8192
flavor11 8 16384
flavor12 8 32768
flavor13 16 16384
flavor14 16 32768
flavor15 16 65536
备注:flavor名称 CPU核数 内存大小(MB)
且要求预测全部15种虚拟机的数量
物理机规格为56核CPU、128G内存
需要优化的资源维度为CPU
则预测效果为:
可见按FF算法和BFD算法装填需要17台物理机
而模拟退火算法只需要15台物理机
代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int FlavorCPU[20]={0,1,1,1,2,2,2,4,4,4,8,8,8,16,16,16}; //15种型号虚拟机的CPU规格 单位为核
int FlavorMEM[20]={0,1,2,4,2,4,8,4,8,16,8,16,32,16,32,64};//15种型号虚拟机的内存规格 单位为G
int PredictRes[20]={0,30,18,4,14,34,2,6,36,14,4,20,12,2,6,2};//下一个时间段的虚拟机预测结果
int vir_cnt=15; //需要预测的虚拟机种类数量
int ChosenType[20];//被选中要预测的虚拟机种类
int phy_cpu=56,phy_memory=128;//物理机的统一规格为56核CPU、128G内存
string task="CPU"; //任务是使CPU资源占用率最小
struct PhyMachine//一台物理机
{
int RemainingCPU,RemainingMEM;//剩余资源量
int VirItem[20];//VirItem[i]代表这台物理机中装载的种类i虚拟机的数量
PhyMachine(){
for(int i=0;i<20;i++) VirItem[i]=0;
RemainingCPU=phy_cpu;
RemainingMEM=phy_memory;
}
};
class InstallCases//一个装填样例
{
public :
vector phymachine;//这种装填方法总共要用多少台物理机
vector flavors; //待安装的虚拟机序列
double point; //对此装填样例优劣的评判分数,分数越低、效果越好
void Install() //用FF方法按虚拟机序列顺序安装虚拟机
{
PhyMachine p;
phymachine.push_back(p);//先塞一个空的物理机进去
for(int i=0;i<(int)flavors.size();i++)
{
int flavortype=flavors[i];
bool flag_enough=false;
for(int j=0;j<(int)phymachine.size();j++)
{
if(phymachine[j].RemainingCPU>=FlavorCPU[flavortype]&&phymachine[j].RemainingMEM>=FlavorMEM[flavortype])
{
phymachine[j].RemainingCPU-=FlavorCPU[flavortype];
phymachine[j].RemainingMEM-=FlavorMEM[flavortype];
phymachine[j].VirItem[flavortype]++;
flag_enough=true;
break;
}
}
if(flag_enough==false){//说明物理机不够用,新开一个物理机
PhyMachine p;
p.RemainingCPU-=FlavorCPU[flavortype];
p.RemainingMEM-=FlavorMEM[flavortype];
p.VirItem[flavortype]++;
phymachine.push_back(p);
}
}
}
double J()//计算当前这个装填方式的分数point,分数越低越好
{
//分数=(目前物理机数-1) +最后一个物理机的资源占用率
point=(double)(phymachine.size()-1);
if(task=="CPU"){
double freecpu=(double)phy_cpu-(double)phymachine[phymachine.size()-1].RemainingCPU;
point+=freecpu/(double)phy_cpu;
}
else if(task=="MEM"){
double freemem=(double)phy_memory-(double)phymachine[phymachine.size()-1].RemainingMEM;
point+=freemem/(double)phy_memory;
}
return point;
}
};
vector Cases;//存储不同的装填方式
void Install()
{
double T=100.0,T_min=1,r=0.995;//分别是起始温度、停止温度 、温度下降速率
double J_min,J_cur,dE; //分别是目前最好的分数、当前安装样例的分数 、前面两者的差值
//首先要把PredictRes这个数组里的东西展开转换成虚拟机序列
vector vi_flavors;
for(int i=0;i dice;
for(int i=0;i<(int)vi_flavors.size();i++){
dice.push_back(i);
}
InstallCases Case0;//原始装填案例,相当于直接FF装填
Case0.flavors=vi_flavors;
Case0.Install();
J_min=Case0.J();
cout<<"按原始虚拟机序列装填所得分数:"<T_min)
{
random_shuffle(dice.begin(),dice.end());//随机打乱这个骰子向量里的元素顺序
vector new_vi_flavors=vi_flavors;
swap(new_vi_flavors[dice[0]],new_vi_flavors[dice[1]]);//随机交换虚拟机序列中的两个虚拟机的位置
// for(int i=0;i random_num ){//T越大 (dE/T)越接近1,就越有可能接受这个方案
temcnt1++;
vi_flavors=new_vi_flavors;//下一次循环会以这个数组为基础进行随机变化
Cases.push_back(Case) ;
J_min=J_cur;
}
}
T=r*T;//降温
}
cout<<"得到更差的序列的次数:"<FlavorCPU[b]);
}
bool cmp_mem(int a,int b)
{
return (FlavorMEM[a]>FlavorMEM[b]);
}
int BFD()
{
if(task=="CPU") sort(ChosenType,ChosenType+vir_cnt,cmp_cpu);
else if(task=="MEM") sort(ChosenType,ChosenType+vir_cnt,cmp_mem);
InstallCases Casebfd;
PhyMachine p;
Casebfd.phymachine.push_back(p);//先放一个空物理机进去
for(int i=0;i=FlavorCPU[temflavor]&&Casebfd.phymachine[k].RemainingMEM>=FlavorMEM[temflavor])
{
if(first==true){
minspace_index=k;
first=false;
}
else if(task=="CPU"&&Casebfd.phymachine[k].RemainingCPU