资讯动态

华为软挑复赛:资源调度与成本优化的算法工程实践

发布时间:2026/8/21 3:42:32 来源:尧图企业网站定制
1. 项目概述与核心挑战2022年的华为软件精英挑战赛我印象特别深刻。那年我带着团队一路从初赛杀进复赛整个过程可以说是对算法、工程和心态的三重考验。这个比赛本质上是一个资源调度与成本优化的综合性问题它不像纯粹的算法竞赛只追求理论最优解而是要求你将算法模型落地成一个高效、稳定、可扩展的工程系统最终在给定的约束下实现总成本通常是购买服务器的资本支出和运行能耗的操作支出之和的最小化。复赛阶段问题规模、约束条件和对手的策略都会急剧复杂化单纯的“手撕算法”已经不够看了必须有一套清晰的、从顶层设计到底层实现的完整思路。那年赛题的具体背景是云数据中心的虚拟机部署与调度。你需要处理一批随时间动态到达的虚拟机创建、删除请求称为“任务流”并将它们合理地安置到有限的物理服务器上。目标是在满足所有虚拟机资源需求CPU、内存和服务等级协议比如不能跨服务器部署的“亲和性”约束、必须跨服务器部署的“反亲和性”约束的前提下最小化采购服务器的总成本。服务器有多种型号采购成本和能耗模型不同这就像给你一笔预算去建材市场买不同规格的集装箱服务器然后需要把一堆形状大小各异、还有特殊摆放要求的货物虚拟机塞进去既要尽可能用满集装箱又要让总花费买箱子的钱电费最少。复赛的核心挑战在于“动态”与“全局”的平衡。任务流是未知的、在线到达的你无法预知未来所有请求。同时虚拟机有生命周期创建后运行一段时间会被删除释放资源。这就引出了几个关键矛盾一是探索与利用的权衡——是应该为了未来可能到来的大虚拟机而预留资源探索还是优先满足当前请求、提高现有服务器的利用率利用二是局部最优与全局最优的冲突——把一个虚拟机放在当前最“合适”的服务器上可能会碎片化资源阻碍后续更大虚拟机的部署导致最终需要购买更多服务器。三是算法复杂度与运行时延的博弈——你可以设计非常复杂的全局搜索或重调度算法来寻找更优解但比赛有严格的时间限制超时就意味着失败。我们的整体思路是构建一个分层决策框架上层是一个宏观调度器负责做粗粒度的决策比如为新到达的虚拟机快速分配一个目标服务器组或区域下层是微观放置器在目标区域内执行精细的资源匹配与装箱算法。同时引入一个异步重调度引擎周期性地检查系统状态对已部署的虚拟机进行“腾挪”以优化资源碎片、降低长期成本。这个框架的核心在于通过分层将问题的复杂度分解并利用重调度来纠正在线决策的短视缺陷。2. 核心算法策略选型与解析面对这样一个NP-Hard的装箱与调度问题没有银弹算法。我们的策略是组合拳针对不同场景和决策阶段选用或改造最合适的算法。2.1 在线放置贪心策略与评分函数的艺术对于实时到达的虚拟机创建请求我们必须在线做出放置决策。这里贪心算法是主流选择但“贪心”的策略决定了效果。我们放弃了最简单的首次适应First-Fit或最佳适应Best-Fit而是采用了多维度加权评分函数。我们为每一台候选服务器设计了一个评分分数越高代表放置当前虚拟机到该服务器“未来可能造成的负面影响”越小或者说“性价比”越高。评分函数Score(server, vm)通常考虑以下几个核心因素并通过权重系数α, β, γ...来调节其重要性资源平衡度Resource Balance放置后服务器CPU和内存的剩余比例是否均衡。极端不均衡如CPU剩90%内存剩10%会产生碎片导致后续虚拟机无法安置。常用计算公式是放置后abs(剩余CPU比例 - 剩余内存比例)值越小越好。我们将其取负后纳入评分。资源紧凑度Resource Compactness鼓励将虚拟机放在剩余资源更少的服务器上这样可以更快填满一台服务器使其进入低功耗状态或避免开启新服务器。可以用(剩余CPU 剩余内存)来衡量值越小评分越高。成本效率Cost Efficiency考虑服务器本身的单位资源成本。对于异构服务器将虚拟机放在“每单位资源采购成本更低”的服务器上可能长期更优。这部分计算需要结合服务器型号的单价和资源总量。约束惩罚项Constraint Penalty对于有亲和性/反亲和性约束的虚拟机评分函数需要大幅提高或降低对应服务器的分数。例如对于必须与虚拟机A在同一服务器的虚拟机B只有已经部署了A的服务器才能获得正常评分其他服务器评分为负无穷。我们的评分函数最终形态类似于Score α * ( - |ΔCPU - ΔMEM| ) β * ( - remaining_total ) γ * cost_per_unit_resource δ * constraint_bonus/penalty实操心得权重系数α, β, γ, δ的调参是比赛胜负手之一。我们通过历史数据回放用初赛数据训练和自动参数搜索如网格搜索或简单的贝叶斯优化来寻找最优组合。一个关键发现是在比赛前期服务器空置多应更侧重成本效率优先选用便宜机型在比赛中后期资源紧张应更侧重资源平衡度以减少碎片。2.2 离线优化与重调度搜索算法与元启发式在线贪心决策是短视的。为了系统性地优化全局成本必须定期进行重调度Remapping即把一些已部署的虚拟机从当前服务器迁移到其他服务器从而腾空或整合出一些服务器来关机省电或者优化资源布局以减少未来购买需求。重调度问题是一个标准的二次分配问题复杂度极高。我们采用了改进的鲸鱼优化算法WOA与局部搜索的混合策略。这里简单解释一下我们的思路问题编码将一个调度方案编码为一个向量向量长度等于虚拟机数量每个元素的值代表该虚拟机被分配到的服务器ID。初始种群生成不是完全随机生成。我们以当前在线部署方案为“精英个体”再通过随机扰动随机选择一部分虚拟机改变其服务器生成其他初始个体保证种群质量。改进的鲸鱼包围与狩猎机制标准WOA模拟鲸鱼气泡网捕食。我们引入了一个全局搜索增强因子。在算法迭代初期增大随机搜索和探索的比例避免过早陷入由当前在线方案形成的局部最优在迭代后期逐步加强局部开发围绕当前最优解进行精细调整。局部搜索嵌入在WOA的每次迭代后对当前最优的几个个体进行局部搜索。例如随机选择两个服务器尝试交换它们上面的虚拟机或者将一个服务器上的所有虚拟机整体迁移到另一个服务器如果成本降低则接受。这个操作能快速提升解的质量。可行性修复算法产生的随机解可能违反资源约束或亲和性约束。我们设计了一个快速的修复算子例如如果某服务器超载则按一定策略如迁移能耗最大的虚拟机将其上的虚拟机迁移到其他有容量的服务器直至满足约束。注意事项重调度不能太频繁因为虚拟机迁移本身在现实中有开销虽然赛题可能不考虑。我们设置了一个触发阈值例如当系统整体资源碎片化程度超过某个值或者每隔固定数量的请求批次才执行一次重调度。同时重调度的搜索时间必须严格控制我们使用迭代次数或时间作为终止条件。2.3 资源预测与预留应对不确定性为了缓解在线算法的短视问题我们尝试了简单的资源需求预测。虽然比赛不提供未来请求信息但我们可以从历史请求流中学习一些粗粒度的模式。我们维护一个滑动窗口统计最近一段时间内到达的虚拟机的资源规格分布例如大CPU小内存、小CPU大内存、均衡型等各占多少比例。当一个新的请求到达时除了用评分函数为其寻找最佳位置我们还会评估如果把它放在服务器A那么服务器A剩余的资源形态是否与历史资源需求分布“匹配”比如历史数据显示未来很可能来很多需要大内存的虚拟机那么当前一个请求如果消耗了大量内存而剩余很多CPU这个决策就值得商榷。我们用一个简单的匹配度分数来量化这一点并将其作为评分函数的一个附加项。这相当于一个基于经验的、轻量级的“前瞻”机制在实践中对减少碎片有不错的效果。3. 工程架构与性能优化再好的算法跑不出来也是零分。复赛的数据规模要求你的代码必须高效。我们的工程实现围绕以下几个核心点展开。3.1 核心数据结构设计高效的数据结构是快速决策的基础。我们主要优化了服务器和虚拟机集合的查询与更新操作。服务器集合索引我们维护多个服务器索引例如按机型索引的服务器列表方便按型号进行资源查询。按剩余CPU/内存大小的倒排索引使用平衡二叉搜索树如Cstd::set或跳表以剩余CPU或内存为键存储服务器指针。当需要寻找能满足某个虚拟机资源需求的服务器时可以快速进行范围查询lower_bound将时间复杂度从O(N)降到O(log N)。按评分缓存的有序列表对于每种常见的虚拟机规格可以预计算并缓存所有能容纳它的服务器的评分并排序。当该规格虚拟机到达时直接取列表头部服务器即可。但这需要处理服务器资源变化后的缓存更新问题。虚拟机信息存储使用unordered_map(ID - 虚拟机信息) 实现O(1)的查找。虚拟机信息结构体包含其规格、所属服务器ID、创建时间等。踩坑记录最初我们只用一个列表存储服务器每次放置都需要遍历所有服务器计算评分在复赛规模下直接超时。引入按资源倒排索引后性能提升了两个数量级。另一个坑是缓存更新当服务器资源发生变化虚拟机部署或删除必须同步更新所有相关的索引和缓存否则会导致数据不一致产生“资源超售”的错误。我们采用了观察者模式服务器资源状态变更时自动通知所有索引管理器进行更新。3.2 请求批处理与异步重调度读入请求流后我们并不严格按顺序一个请求处理一个请求地输出。而是采用微批处理策略。输入批处理一次性读入一个时间窗口的所有请求比如未来100个请求。预处理与排序对这些请求进行预处理例如将删除请求和创建请求分离。对于创建请求可以按虚拟机规格如按资源总量降序进行排序。“先大后小”的放置策略是装箱问题的经典启发式能有效减少碎片。批量决策对这批排序后的创建请求依次进行放置决策。由于我们有了未来一小段时间的请求信息尽管不是全部可以在做当前决策时稍微“照顾”一下接下来的请求例如在评分函数中给予资源平衡度更高的权重。输出与状态更新批量生成决策输出然后批量更新所有服务器的资源状态。这比单条更新减少了大量索引维护的开销。重调度模块完全异步化。主线程负责处理在线请求流和批处理决策。我们另起一个守护线程或定时任务每隔一段时间或当主线程触发标志时对当前系统状态做一个快照然后在这个快照上运行耗时的改进WOA算法。重调度算法运行完毕后会产生一个迁移计划一组虚拟机ID, 目标服务器ID对。主线程在下一个安全的时间点例如处理完当前批次请求后原子性地应用这个迁移计划并更新所有状态。注意异步重调度需要处理好并发数据访问。我们采用读写锁shared_mutex主线程以写模式更新状态重调度线程以读模式获取快照避免脏读。3.3 评估与调试体系在比赛中快速验证想法至关重要。我们搭建了一个本地评估体系标准输入/输出重定向将官方提供的训练用例输入我们的程序捕获输出然后与一个简单的标准调度器如首次适应算法的结果进行对比快速验证正确性。成本计算与可视化脚本用Python写了一个脚本解析我们的输出日志和最终购买的服务列表计算总成本并绘制资源利用率随时间变化的曲线、服务器碎片分布图等。可视化能直观地暴露问题比如在哪段时间碎片化严重哪种服务器型号利用率低下。A/B测试框架对于评分函数权重的调整、是否开启重调度、重调度触发阈值等参数我们设计了一套A/B测试流程。用同一段历史请求流跑不同参数配置的程序对比最终成本和运行时间用数据驱动决策。性能剖析Profiling使用gprof或Valgrind工具分析代码热点。我们发现大部分时间花在了评分函数的计算和索引的更新上。于是我们将评分函数中一些可以预计算的部分如服务器型号的基础成本系数提前算好存起来并优化了内存布局减少缓存未命中。4. 实战复盘与避坑指南回顾整个复赛历程有几个关键点直接决定了成绩上限。4.1 常见问题与排查技巧问题输出结果错误判题系统返回“非法操作”。排查这是最令人头疼的错误。我们建立了一个严格的检查清单资源超售确保每次部署和迁移后服务器的已用资源不超过总资源。在每次更新服务器状态的前后加入断言Assertion。亲和性/反亲和性约束在部署和迁移时检查目标服务器上已有的虚拟机ID确保满足约束条件。我们为每个服务器维护了一个部署的虚拟机ID集合和规格集合用于快速检查。虚拟机ID有效性确保处理的虚拟机ID是已创建且未被删除的。删除一个不存在的虚拟机会导致错误。双端部署对于双节点部署的虚拟机其CPU/内存需求是单台服务器的两倍必须确保两台目标服务器都有足够资源。技巧在代码中实现一个sanity_check()函数在每批次请求处理完后遍历所有服务器和虚拟机检查上述所有约束是否满足。在开发阶段始终开启这个检查虽然牺牲一点性能但能快速定位隐蔽的Bug。问题程序运行超时。排查算法复杂度确认核心循环的时间复杂度。放置一个虚拟机的操作不应是O(N)而应借助索引达到O(log N)或近似O(1)。I/O瓶颈使用C的ios::sync_with_stdio(false)和cin.tie(nullptr)来关闭C与C流之间的同步大幅提升输入输出速度。使用getline和字符串解析代替频繁的cin 。不必要的拷贝传递大型结构体时使用常引用const 。避免在热循环中动态分配内存如new,vector::push_back可能导致扩容。重调度线程失控确保重调度算法有明确的迭代次数或时间上限防止其陷入死循环或计算时间过长拖垮主线程。技巧在本地用最大的测试用例进行压力测试同时用性能剖析工具定位热点。问题成本始终比排行榜前列的队伍高不少。排查服务器采购策略是否过于保守过早购买了昂贵的服务器型号或者过于激进买了大量便宜但利用率低的服务器我们引入了服务器开启的延迟决策机制即使当前所有已开机服务器都无法安置新虚拟机也不一定立即购买新服务器。而是先将这个虚拟机放入一个“等待队列”尝试对已开机服务器执行一次紧急的、小范围的重调度只迁移少数虚拟机看是否能腾出空间。如果不行再购买。这能有效减少服务器总数。评分函数失效在比赛后期资源高度碎片化原有的评分函数可能不再适用。我们准备了几套不同参数权重的评分函数根据系统整体的平均资源利用率动态切换。例如当平均利用率80%时切换到更强调“资源平衡度”的权重组。未利用能耗模型服务器的能耗通常与CPU利用率非线性相关如利用率低于50%时能耗较低。在重调度时可以尝试将虚拟机从低利用率服务器合并到高利用率服务器让一些服务器空出来进入低功耗或关机状态。我们会在重调度的目标函数中显式地加入“最大化关机服务器数量”这一项。4.2 那些“教科书不会写”的实战技巧“热启动”你的重调度算法不要每次重调度都从随机解开始。把上一次重调度得到的最优解或者当前在线部署的方案作为本次重调度算法的初始解。这样算法可以在一个较好的起点上进行优化收敛更快在有限时间内找到更好的解。为特殊规格虚拟机开辟“绿色通道”对于那种资源需求特别大或特别偏科如CPU极高、内存极低的虚拟机常规的评分函数可能很难为它们找到合适的位置容易触发新服务器购买。我们为这类“棘手”的虚拟机维护了一个专属的服务器候选列表这些服务器是特意预留的、资源形态与之匹配的比如专门存放高CPU虚拟机的服务器。当这类虚拟机到达时优先尝试放入专属列表不行再走通用流程。这相当于一种手动的资源分区策略。引入随机性打破僵局当评分函数计算出前几名服务器的分数非常接近时不要总是选择第一名。可以以一定的概率比如10%从前三名中随机选择一台。这相当于在贪心算法中注入了一点随机性有助于避免算法陷入某种固定的、可能非最优的决策模式增加探索能力。日志是你的第二双眼睛除了最终成本要详细记录关键决策点的日志。例如“在时刻T因为一个双节点大规格虚拟机到达触发新购服务器型号为X。” “在时刻T重调度模块运行迁移了20个虚拟机成功关闭了3台服务器。” 分析这些日志你能清楚地知道成本是在哪个环节被推高的从而有针对性地优化策略。参加这类竞赛到最后比拼的往往不是某个高深的算法而是对问题本质的理解深度、工程实现的稳健性以及快速迭代和调优的策略能力。从设计评分函数权重的细枝末节到构建异步重调度的整体架构每一个环节都需要反复推敲和实测。最大的体会是没有一个一劳永逸的“最优解”最好的策略是一个能够根据系统状态动态调整的、鲁棒的智能体。这也正是“调度”与“成本优化”在现实工业场景中的魅力与挑战所在。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价