【比赛总结】软件挑战赛

前言

这算是本人第一次参加正式参加华为的比赛,去年在外围观望各位大佬,参加了比华为难度低一些的中兴算法笔试,今年的题目相对于往年难度较低,对于数据结构有一定的高度,但最重要的还是多种多样的解题思路,可能同样的算法构建难度,较好的解题思路缺能起到很好的效果。本次的成绩是初赛:练习赛第10,正式赛第11;复赛:练习赛第13,正式赛第17。由此可以看出该赛区的竞争激烈,本队维护的代码基本框架是具有一定普适性,但要得到更高的分数需要在某些方面进行约束 后期更改的数据集的对于维护的代码不太优化,慢慢的变成了调参细节优化。最后的结果还是比较满意的,拿到了一个奖项和笔试的绿卡,既提升代码能力,也有所收获。

本届赛题:

简述:云计算场景中的资源的规划和调度优化问题。预设有不同类型的服务器与虚拟机种类,给定一段时间内用户的申请、删除虚拟机请求序列,要求优化虚拟机迁移、云服务器购买、虚拟机部署等步骤,使得在满足用户需求的情况下总成本最低。
详细题目可到华为官网寻找:链接
小技巧:对于这类比赛本地有判题器和可视化软件,可以大大的提高版本迭代和优化的效率;
1)、判题器的编写:需根据题意来进行逻辑上的编写,与规则的错误判读并输出相应的错误;
如以下的几段代码检测资源是否异常:

//检查合理性
void check_ret_exe() {
	int len = Server_list.size();
	Server_cpures = 0;
	Server_ramres = 0;
	for (int i = 0; i < len; i++) {
		Server_cpures += Server_list[i].node_a.cpu_now + Server_list[i].node_b.cpu_now;
		Server_ramres += Server_list[i].node_a.ram_now + Server_list[i].node_b.ram_now;
		if (Server_list[i].node_a.cpu_now<0 || Server_list[i].node_a.cpu_now>Server_list[i].node_a.cpu_max) {
			fout << "服务器" << Server_list[i].id << "A节点CPU资源不合理" << endl;
		}
		if (Server_list[i].node_b.cpu_now<0 || Server_list[i].node_b.cpu_now>Server_list[i].node_b.cpu_max) {
			fout << "服务器" << Server_list[i].id << "B节点CPU资源不合理" << endl;
		}
		if (Server_list[i].node_a.ram_now<0 || Server_list[i].node_a.ram_now>Server_list[i].node_a.ram_max) {
			fout << "服务器" << Server_list[i].id << "A节点RAM资源不合理" << endl;
		}
		if (Server_list[i].node_b.ram_now<0 || Server_list[i].node_b.ram_now>Server_list[i].node_b.ram_max) {
			fout << "服务器" << Server_list[i].id << "B节点RAM资源不合理" << endl;
		}
	}
	vm_cpures = 0; vm_ramres = 0;
	for (auto i : VMware_list) {
		vm_cpures += i.second.cpu;
		vm_ramres += i.second.ram;
	}
	if (vm_cpures != Server_cpures) fout << "服务器与虚拟机cpu资源搭配不合理" << endl;
	if (vm_ramres != Server_ramres) fout << "服务器与虚拟机RAM资源搭配不合理" << endl;
}

运行脚本:运用一些运行时间计算、输入输出重定向,预处理加速优化,python脚本进行代价计算等,来提供开发的效率。
运行时间计算及输入输出重定向:

function getTiming() {
    start=$1
    end=$2
    start_s=$(echo $start | cut -d '.' -f 1)
    start_ns=$(echo $start | cut -d '.' -f 2)
    end_s=$(echo $end | cut -d '.' -f 1)
    end_ns=$(echo $end | cut -d '.' -f 2)
    time=$(( ( 10#$end_s - 10#$start_s ) ))
    echo "$time s"
}
g++ -g -o 1 codev1.cpp   
start=$(date +%s.%N)
./1.exe < training-1.txt > training-1_out.txt
end=$(date +%s.%N)
runtime=$(getTiming $start $end)
echo "runtime: "$runtime

预处理加速优化:

#pragma GCC optimize(3,"Ofast","inline") //详细的介绍可自行查阅

python脚本进行代价计算:
在shell脚本中进行一些复杂的计算与处理较为麻烦,一般使用python来调用高级库开发效率较高
本项目只需进行一些简单的计算:

#coding=utf-8
cost1 = 0
cost2 = 0
cost3 = 0
cost4 = 0
for i in range(2):
    f = open('log_'+str(i + 1) + '.txt', 'r', encoding="utf-8")
    s1 = f.readline().strip().split(':')
    s2 = f.readline().strip().split(':')
    s3 = f.readline().strip().split(':')
    s4 = f.readline().strip().split(':')
    cost1 += int(s1[1],base=10) 
    cost2 += int(s2[1],base=10) 
    cost3 += int(s3[1],base=10) 
    cost4 += int(s4[1],base=10) 
    print('数据集', i + 1, '总成本:', s1[1])
    print('数据集', i + 1, '硬件成本:', s2[1])
    print('数据集', i + 1, '能耗成本:', s3[1])
    print('数据集', i + 1, '迁移次数:', s4[1])
print('总成本:', cost1)
print('硬件成本:', cost2)
print('能耗成本:',  cost3)
print('总迁移次数:',  cost4)

数据的可视化使用python调用一些画图库,图行库当然是最好的选择,这部分较为复杂也不是重点就不细说了。

关于比赛思路

本赛题可分为四个处理过程去考虑:1.建立数据结构 2.购买服务器 3.分配虚拟机 4.迁移虚拟机
总体思路:前期通过查看了装箱问题、背包、整数规划、还要华为往年真题(如18中就有装箱问题)、百度18年也举办过相关的调度算法比赛,在其中有很多可借鉴的东西。最终确定的基本思想:使得总成本低——>服务器的使用率高(需要购买的服务器少了,空闲的服务器也多了),从而问题的目标函数转化为提高服务器使用率。对于背包与贪心算法进行比较,背包的利用率会比贪心的高,但其算法复杂度高,对于大的数据集,是容易超时的。这时选用贪心的想法,贪心是一种策略,具体的代码实现要有贪心的目的(提高服务器使用率)与相应的评价函数(对于放入该服务器以后的资源利用率的评价函数)。经过试验与查找质料,确定了降序最佳适应法的贪心策略装箱实现代码架构。在迁移方面也是以提高服务器的利用率为目标,思路大致为将资源利用率较小的服务器里的虚拟机放入其他服务器。在购买服务器方面,由于采用的贪心算法,故服务器的ram与cpu的资源要较为均衡。

1.建立数据结构

建立如下的服务器的类,方便以后的维护和扩展;

//Server  服务器类 自己购买的服务器类
class Server
{
public:
	int id = 0;	  //服务器编号
	string  Version = "";   //服务器型号
	int cpu = 0;  //CPU核数
	int ram = 0;  //内存大小
	int HDcost = 0;	//硬件成本
	int	Ecost = 0;  //每天的能耗成本
	Server_node node_a;
	Server_node node_b;
	vector<int> vmID;//该服务器上部署的虚拟机列表
	//方法
	void add_VMware(VMware a, int status_code);
	void del_VMware(VMware b);
	int check_VMware(VMware c);
	int check_VMware1(VMware c);
	int check_VMware2(VMware c);
	void set_Server(Server_type d);
};
//使用集合进行储存
vector<Server>	Server_list;

2.购买服务器

通过查看数据集中预设的服务器类型,可发现服务器的cpu与ram的资源配置大部分,购买服务器影响的是资源大小和成本,最理想的是按照单位ram与cpu最小成本,即高性价比。但实际情况却不是的,经过多次实验,以能耗进行比较是最佳方案。考虑到总体的能耗成本和开机天数有关联,后续可以进行考虑。同时也要注意购买时服务器的资源要均衡。
比较代码:

//挑选服务器排序规则
bool Server_types_cmp(const  Server_type& a, const  Server_type& b) {
	return  a.Ecost < b.Ecost;
}
//挑选服务器
能放下就放下,二种资源尽量要均衡

3.分配虚拟机

降序最佳适应法:对虚拟机进行从大到小排序,以该虚拟机放入现有资源的服务器以后的评价函数进行记录,循环可放入的全部服务器,选择评价最高的进行放入操作。
对于请求的处理:由于删除请求的存在,请求的整体排序是不符合规则的,因此在不打乱删除请求的前提下,对全部请求按照删除请求进行分层排序。
评价函数:

//返回评价数值
double  check_add(VMware VMware_temp, int i, int flag) {
	double  c5 = ((Server_list[i].node_a.cpu_now + VMware_temp.cpu)*l1 + (Server_list[i].node_a.ram_now + VMware_temp.ram)*l2)*1.0 / (Server_list[i].node_a.cpu_max*l1 + Server_list[i].node_a.ram_max*l2);
	double  c6 = ((Server_list[i].node_b.cpu_now + VMware_temp.cpu)*l1 + (Server_list[i].node_b.ram_now + VMware_temp.ram)*l2)*1.0 / (Server_list[i].node_b.cpu_max*l1 + Server_list[i].node_b.ram_max*l2);
	double  c7 = max(((Server_list[i].node_a.cpu_now + VMware_temp.cpu / 2)*l1 + (Server_list[i].node_a.ram_now + VMware_temp.ram / 2)*l2)*1.0 / (Server_list[i].node_a.cpu_max*l1 + Server_list[i].node_a.ram_max*l2), ((Server_list[i].node_b.cpu_now + VMware_temp.cpu / 2)*l1 + (Server_list[i].node_b.ram_now + VMware_temp.ram / 2)*l2)*1.0 / (Server_list[i].node_b.cpu_max*l1 + Server_list[i].node_b.ram_max*l2));

	double   cost_temp;
	if (flag == 1) cost_temp = c5;
	else if (flag == 2) cost_temp = c6;
	else {
		cost_temp = c7;
	}
	return  cost_temp;
}

4.分配虚拟机

思路:对现有的全部虚拟机进行资源利用率选择,选择利用率最低的服务器,将其服务器中的虚拟机拿出来安装插入的思路重新放入。从而达到整合资源,提供整体服务器资源利用率。
注意:迁移时间复杂度较高,需要设置好退出条件
主要代码:

	for (int i = 0; i < Server_list.size(); i++) {
			useRate[i] = double(Server_list[i].node_a.cpu_now + Server_list[i].node_a.ram_now + Server_list[i].node_b.cpu_now + Server_list[i].node_b.ram_now) / (Server_list[i].cpu + Server_list[i].ram);

			if (Server_list[i].vmID.size() == 0) {
				useRate[i] = DBL_MAX;//当前服务器的虚拟机都被迁走了,所以利用率为0	
				vis[i] = false;  //已经进行过迁移的就不能被迁移
				continue;
			}

			if (vis[i] == true && min_useRate > useRate[i]) {   //找到资源利用率最小的服务器
				min_useRate = useRate[i];
				min_index = i;
			}
		}

总结

本人的主要工作
1、建立可扩展的数据结构,构建baseline,方便后续更新迭代;
2、编写本地判题器与可视化程序用于代码调试;
3、对于虚拟机部署尝试了贪心、动态规划、模拟退火、二维装箱等优化策略,最终采用贪心的思想,以资源利用率为评价函数,借鉴内存分配中的降序最佳适应算法 ,同时剪枝避免不同维度约束情况;
4、综合考虑CPU和 RAM两个维度设计虚拟机迁移策略,设置相应的评价函数,为降低运行时间,将计算分组引入并行计算;
5、对服务器类型、虚拟机类型、请求序列进行数据分析,定义购买服务器的性价比函数;

源代码还在整理,文章还有少许不完善的地方,大家随意看看,等闲下来了再来更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BNUEP Offline Judge 北京师范大学珠海分校离线评测系统是在具备题目测试数据的情况下,能无联网自动评测ACM/ICPC模式的源代码评测系统(即本地测试工具、评测机)。它主要有以下功能(所有的功能都无需联网,在本机即可实现): *评测核心功能: 基本具备Online Judge的判题核心功能,如编译代码、内存限定,时间限定,获取代码长度等; *支持多种语言: 1.0 Beta2版本支持C/C++、Pascal、C#、JAVA; *出题模式 可以在有标准输入数据和标准程序的情况下,由系统产生标准输出数据,并可批量保存,同时自动命名标准输出数据的后缀; *文本高亮对比 在判题后,可以直接在本系统中将自己的程序输出和标准输出进行高亮的文本差异对比,操作类似于一些文本对比软件,在一定程度上可以较方便地发现WA代码的出错细节; *支持不限时执行代码 这个功能可以在一定程度上检测TLE代码的算法是否正确的,当然,不能是跑一天都没跑出来的程序; *打包与加密测试数据 使用加密后的数据可以正常判题,但不显示标准输出。这个功能是为了弥补放出去给别人评测的测试数据是明文的缺陷。加密之后评测方就看不到测试数据。这样就既可以实现离线评测,又可以实现Online Judge上的对测试数据屏蔽; ACM-ICPC简介: ACM国际大学生程序设计竞赛(简称ACM-ICPC)是由国际计算机界具有悠久历史的权威性组织ACM学会(Association for Computing Machinery)主办,是世界上公认的规模最大、水平最高、参与人数最多的大学生程序设计竞赛,其宗旨是使大学生能通过计算机充分展示自己分析问题和解决问题的能力。 ACM-ICPC的每一道题,都具备题目、需求描述、输入格式描述、输出格式描述、样例输入和样例输出共六大信息,有些题目还有一定的提示。此外,裁判还额外存储了关于该题的一组或多组对选手屏蔽的标准输入和标准输出数据,这些测试数据已经经过验证符合题意要求。当用户提交一道题目的源码之后,裁判会将该源码放入评测系统中编译运行,并使用标准输入作为用户程序的输入,然后获取用户程序的输出,接着,将用户程序输出和标准输出比较,最后返回给用户一个评判结果。评判结果包括:Accepted(测试通过)、Compile Error(编译失败)、Memory Limit Exceed(内存超出限制)、Presentation Error(格式错误)、Runtime Error(运行时错误,可能是数组越界,改写只读的内存,除零,栈或堆溢出等错误)、Time Limit Exceed(时间超出限制)、Wrong Answer(答案错误)等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值