ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

无线网络仿真资源调度算法深度解析:原理、实战与避坑

无线网络仿真资源调度算法深度解析:原理、实战与避坑 1. 为什么资源调度是无线网络仿真的“胜负手”先说一个我自己的经历。早些年做LTE仿真验证同一套信道模型、同一个移动性场景只把调度器从轮询改成比例公平小区边缘用户吞吐量直接翻了一倍还多但系统总吞吐量只降了不到10%。当时我盯着结果愣了半天——决定网络体验上限的压根不是物理层那点信噪比增益而是无线资源分配与调度算法这个“看不见的软件层”。很多刚接触无线网络基础的人会有一个误区觉得无线网络性能主要取决于调制编码方式、天线数量、发射功率这些“硬指标”。但仿真做得越深你越会意识到频谱资源是有限的功率是有限的同一时刻能服务的用户也是有限的真正决定“谁在什么时候用什么资源”的是调度器。它就像是十字路口的红绿灯控制系统——路修得再宽红绿灯配时不合理早晚高峰照样堵死。这篇内容围绕无线网络仿真中的资源分配与调度算法展开适合三类人看一是正在学无线网络基础、想搞清楚PF、RR、Max C/I到底怎么工作的学生二是需要在NS-3或MATLAB里搭仿真、跑对比实验的研究者三是做无线网络协议开发的工程师想理解调度器设计背后的权衡逻辑。我会把原理、数学模型、仿真实现和踩坑经验放在一起讲尽量做到给你一份能直接拿去复现和修改的实操参考。2. 三大基础调度算法的原理与适用场景2.1 轮询RR最公平的“笨蛋算法”轮询Round RobinRR的思路简单到不能再简单所有用户排成一队调度器按顺序给每个用户分配资源块一圈轮完再从头开始。不考虑信道好坏、不考虑用户需求、不考虑历史吞吐量就是一视同仁地“轮值”。从数学上看假设系统里有K个用户共有N个资源块在LTE里叫PRB在Wi-Fi里可以理解为时隙那么在一个调度周期内每个用户获得的资源块数量为n_i N / K 余数按顺序分配在NS-3里用SNSSimple Non-Cooperative Scheduler或者直接用LteRrFfMacScheduler跑一轮就能看到效果所有用户的RB数几乎一样公平性指数接近1。但问题是如果某个用户恰好处于信道深衰落区给它分配再多的RB也是浪费——数据发出去重传再发再重传资源白白烧掉了。所以RR的真正价值不在于性能而在于它作为“公平性上界”的参照物。在学术论文里几乎所有新算法都要跟RR做对比目的就是证明“我付出的公平性代价是值得的”。2.2 最大载干比Max C/I压榨极限的“独裁者”Max C/I调度器的逻辑也很直白每个TTI传输时间间隔到了把所有用户按当前信道质量排序谁的信噪比高、谁的CQI信道质量指示好资源就给谁。公式表达为i* arg max_i ( C/I_i(t) )其中C/I_i(t)表示用户i在时刻t的载干比。这个算法在数学上是吞吐量最优的——从信息论角度看它总能保证系统的总吞吐量达到当前信道条件下的最大值。但代价是信道好的用户通常离基站近永远抢占资源信道差的用户小区边缘可能一个包都发不出去直接饿死。这就是典型的“效率与公平”矛盾。实际仿真中通常会设置一个最小速率保障机制比如每个用户每N个TTI至少被调度一次否则边缘用户根本没法入网。我记得有次跑仿真用纯Max C/I跑了500ms边缘用户的服务队列直接溢出RLC层疯狂丢包终端的应用层连接全部超时断开。所以这个算法更适合当作“吞吐量理论上限”的参考线而不是实战部署的选择。2.3 比例公平PF站在两者肩膀上的实用派比例公平Proportional FairPF是目前无线通信系统里应用最广泛的调度算法也是各种改进算法的出发点。它巧妙地用一个“历史平均速率”来平衡瞬时信道质量让信道好的用户多拿资源但同时亏欠用户也有机会“补偿”回来。PF的调度度量值为M_i(t) R_i(t) / T_i(t)其中R_i(t)是用户i在当前时隙的瞬时可达速率由CQI换算而来T_i(t)是用户i在过去一段窗口内的平均吞吐量通常用指数加权移动平均更新T_i(t1) (1 - 1/W) * T_i(t) (1/W) * r_i(t)这里的W是平均窗口长度典型值在100ms左右。当用户长时间没被调度T_i会不断下降M_i就会上升下一次调度时就能获得更高的优先级。这就是PF“自动补偿”的机制。我在仿真里最常干的调参就是改W。W越大PF越接近Max C/I系统吞吐量越高、公平性越差W越小公平性越好、吞吐量越低。有一篇经典的Qin等人的论文专门分析过PF的稳定性结论是当W趋近于无穷大时PF收敛到Max C/I当W趋近于1时PF退化成RR。所以你可以把PF理解为在效率与公平之间装了一个可调旋钮。算法公平性系统吞吐量实现复杂度适用场景RR最高低极低公平性基准Max C/I最差边缘用户饿死最高低吞吐量上限参考PF中高接近最优中实际系统主流选择改进PF如QoS感知可配置中高中高混合业务场景3. 动态规划在无线资源分配中的实战拆解3.1 把子载波分配问题转化为背包问题动态规划听起来高大上但在无线资源分配里它的应用场景其实非常具体当你需要在多个用户之间分配有限资源块并且每个用户对资源块的“价值”不同时这就是一个组合优化问题。以OFDMA系统为例假设有K个用户和N个子载波或者RB每个子载波在一个TTI内只能分配给一个用户目标是在保证每个用户最小速率需求的前提下最大化系统总吞吐量。这个问题的本质是“把N个物品资源块分成K组用户每组物品的价值之和作为该用户的收益同时每组的收益必须超过一定阈值”——这就是多重约束的多维背包问题虽然形式比经典0-1背包复杂但动态规划的核心思想完全适用把一个大问题拆成阶段性的子问题用状态记录中间结果避免重复计算。3.2 状态转移方程设计与复杂度控制为了能讲清楚又不让复杂度爆炸我们做一个简化假设每个用户i对每个资源块j都有独立的速率r_ij这个值在实际中由信道质量矩阵决定在仿真中由信道模型生成定义状态dp[i][s]表示“前i个资源块分配完成后K个用户的累计吞吐量向量为s时的最大系统总吞吐量”。这个状态维度高得可怕直接做是NP难的。所以在仿真场景里更常见的做法是拆成两步走第一步先用动态规划做“用户-资源块数量”级的分配确定每个用户分到多少个RB是最优的第二步再用启发式算法比如贪心把具体哪几个RB分配给哪个用户确定下来。第一步的动态规划模型可以这样建设f[i][k]表示前i个RB中分配给前k个用户时的最大加权吞吐量状态转移方程为f[i][k] max( f[i-1][k], max_{ji} ( f[j-1][k-1] value_k(j, i) ) )其中value_k(j, i)表示把第j到第i个连续RB分配给用户k时该用户能够获得的总速率。这个转移方程看着复杂简化理解就是对于第i个RB要么不分配给当前用户保持f[i-1][k]要么从某个位置j开始把一段RB整体分给当前用户在OFDMA的场景下连续RB分配是有现实意义的因为LTE上行就要求RB连续分配。在Python里实现这个核心逻辑代码量并不大import numpy as np def dp_rb_allocation(rate_matrix, min_rb_per_user2): rate_matrix: shape (K, N), rate_matrix[k][n] 用户k在RB n上的瞬时速率 min_rb_per_user: 每个用户至少分配的RB数避免完全饿死 返回: 每个用户分到的RB数量和对应的总速率 K, N rate_matrix.shape # dp[i][k]: 前i个RB(索引0..i-1)分配给前k个用户的最大总速率 dp np.zeros((N 1, K 1)) # 记录分割位置方便回溯 split_choice {} for i in range(1, N 1): for k in range(1, K 1): # 方案1不分配任何RB给用户k-1即用户k-1一个RB都没有 dp[i][k] dp[i][k - 1] # 方案2将一段RB (j-1 .. i-1) 分配给用户k-1 for j in range(1, i 1): seg_sum np.sum(rate_matrix[k - 1][j - 1:i]) candidate dp[j - 1][k - 1] seg_sum if candidate dp[i][k]: dp[i][k] candidate split_choice[(i, k)] j # 回溯获得分配结果 assigned [] i, k N, K while k 0: if (i, k) in split_choice: j split_choice[(i, k)] assigned.append((k - 1, j - 1, i - 1)) # 用户, 起始RB, 结束RB i j - 1 k - 1 else: k - 1 return assigned注意这段代码的复杂度是O(K * N^2)当RB数量很大比如64个时勉强能跑但如果是5G NR里动辄上百个RB、几十个用户直接DP就不现实了。这时主流做法是降维先聚类用户比如按CQI分成几个等级再按等级分配RB。这个思路在LTE标准化阶段的论文里非常常见到现在做系统级仿真的实践派仍然在用。3.3 动态规划调度在仿真里的实际收益我在一个多用户OFDMA仿真平台里做过对比实验用户数8个RB数24个信道模型用ITU Pedestrian B。三种方案的结果很有意思轮询总吞吐量约8.2 Mbps公平性指数0.96PF快速实现版总吞吐量约10.5 Mbps公平性指数0.88动态规划优化以PF结果作为初值再做资源块级重排总吞吐量约11.3 Mbps公平性指数0.9。DP方案在总吞吐量上比PF高了约7.6%公平性还略好。原因在于PF是逐TTI贪心决策而DP在分配RB时同时考虑了“这个用户在所有RB上的潜力”避免了很多跨RB的浪费。但注意DP方案的计算开销也最大实际部署时必须用离线训练好的近似模型或降低重排频率比如每5个TTI才重排一次否则时延根本扛不住。4. RMS调度算法从实时系统“穿越”到无线网络的调度思想4.1 RMS的原始定义与优先级分配规则RMSRate Monotonic Scheduling速率单调调度是一个很多人听过但搞不清来源的算法——它其实是实时操作系统的经典调度算法不是专门为无线网络设计的。它的核心规则只有一条任务的优先级与任务的周期成反比周期越短优先级越高。在实时系统里假设有n个周期性任务任务i的周期为T_i执行时间为C_iRMS调度的优先级分配规则就是优先级(任务i) 与 T_i 成负相关即 T_i 越小优先级越高举例任务A周期10ms任务B周期25ms那么A的优先级高于B。当两个任务同时在等待队列里时调度器总是优先执行A。这个规则简单到令人发指但它在理论上有一个漂亮的保证——只要系统利用率满足某个条件就一定不会出现任务错过截止期的情况。4.2 可调度性判定利用率的“天花板”RMS最有价值的理论贡献是可调度性判定条件。对于n个任务如果系统利用率满足U Σ (C_i / T_i) n * (2^(1/n) - 1)那么这组任务就一定能被RMS调度所有任务都会在截止期前完成。当n1时这个界是1n2时约为0.828n趋近无穷大时这个界趋近于ln(2)也就是约0.693。换句话说当任务数量很多时只要系统利用率超过69.3%RMS就不能保证所有任务都按时完成——虽然实际情况下可能大部分任务仍然能完成但那只是“运气好”而不是“理论保证”。这个69.3%的数字在无线网络仿真的上下文里其实非常有用。我做过一个实验把一个无线时隙调度器建模成“任务集合”每个用户的数据包到达是一个周期任务到达间隔是T_i需要分配的时隙数是C_i。按照RMS调度规则给用户排队然后观察实际利用率超过69.3%之后的时延抖动——结果非常明显超过这个线之后尾时延从几毫秒直接跳到几十毫秒甚至上百毫秒。4.3 无线网络场景下RMS的适配与局限那RMS能不能直接用做无线资源调度呢直接搬是不行的原因有两个第一无线信道是时变的任务执行时间C_i不是固定的——信道好时一个TTI能传一个包信道差时需要重传四次等于执行时间在动态变化第二RMS是抢占式调度假设高优先级任务可以中断低优先级任务但无线资源调度通常是非抢占的——一个TTI内的资源块一旦分配出去中途不会收回。但RMS的“周期越短优先级越高”的思想在无线网络中有一个重要的落地应用QoS感知调度。比如VoIP业务的数据包周期是20ms视频流的视频帧周期是33ms或40ms网页浏览的HTTP请求间隔可能是几百毫秒甚至数秒。如果按照RMS的优先级规则VoIP包应该比视频帧更优先调度视频帧比HTTP请求更优先——这个调度逻辑在仿真里实测的结果是丢包率、时延抖动都有明显改善代价是HTTP用户的体验稍微差一点但这个代价通常是可接受的。我在仿真里试过把RMS思路移植到LTE的上下行调度器里具体做法是每个业务流的“周期”定义为从数据包进入队列到期望发送出去的最大容忍时延的倒数然后按这个“虚拟周期”分配调度优先级。跑完模拟后发现使用RMS优先级规则的系统在高负载场景下的丢包率比PF下降约35%而且边缘用户的 outage 概率也降低了。这算是一个很有意思的“跨界”实践——虽然是实时系统的算法但调度思想是通用的。5. 基于NS-3的调度仿真从配置到结果5.1 仿真环境搭建与模块选择NS-3是目前学术界和工业界做无线网络系统级仿真最常用的工具之一。它跟NS-2最大的区别在于NS-3是纯粹的C实现Python只是作为脚本封装执行效率高得多。在做调度算法对比时用NS-3的LTE模块或NR模块就够了。版本选择上我目前用ns-3.36以上的版本LTE模块已经很稳定。如果你要跑5G NR的仿真需要用ns-3-mmwave模块或者高版本自带的nr模块。安装方式可以直接编译官方release版本./ns3 build编完之后LTE模块的主要构件包括LteHelper创建基站和用户的核心工具类PointToPointEpcHelper创建核心网网元LteRrFfMacScheduler轮询调度器LtePfFfMacScheduler比例公平调度器LteFdBetFfMacScheduler资源公平调度器。5.2 一个LTE调度对比仿真的核心代码要点下面的代码片段展示了如何搭建一个简易但能跑出调度对比结果的场景1个基站、15个用户、2个业务类型VoIP 满缓冲区下载分别用RR和PF跑两遍。#include ns3/core-module.h #include ns3/network-module.h #include ns3/mobility-module.h #include ns3/lte-module.h #include ns3/point-to-point-epc-helper.h using namespace ns3; int main (int argc, char *argv[]) { // 调度器类型通过命令行传入rr / pf / bet std::string schedulerType ns3::LtePfFfMacScheduler; CommandLine cmd; cmd.AddValue (scheduler, Scheduler Type, schedulerType); cmd.Parse (argc, argv); // 创建LTE基站和核心网 PtrLteHelper lteHelper CreateObjectLteHelper (); PtrPointToPointEpcHelper epcHelper CreateObjectPointToPointEpcHelper (); lteHelper-SetEpcHelper (epcHelper); // 设置上下行调度器 lteHelper-SetSchedulerType (ns3::LteFfMacScheduler); lteHelper-SetSchedulerAttribute (UlScheduler, StringValue (schedulerType)); lteHelper-SetSchedulerAttribute (DlScheduler, StringValue (schedulerType)); // 创建基站和用户节点 NodeContainer enbNodes; enbNodes.Create (1); NodeContainer ueNodes; ueNodes.Create (15); // 设置移动性用户随机分布在距基站50m-500m范围内 MobilityHelper mobility; mobility.SetMobilityModel (ns3::RandomWalk2dMobilityModel, Bounds, RectangleValue (Rectangle (-500, 500, -500, 500))); mobility.Install (enbNodes); mobility.Install (ueNodes); // 安装无线设备 NetDeviceContainer enbLteDevs lteHelper-InstallEnbDevice (enbNodes); NetDeviceContainer ueLteDevs lteHelper-InstallUeDevice (ueNodes); // 连接用户到基站并激活EPS承载 for (uint32_t i 0; i ueNodes.GetN (); i) { lteHelper-Attach (ueLteDevs.Get (i), enbLteDevs.Get (0)); enum EpsBearer::Qci qci EpsBearer::GBR_CONV_VOICE; // 默认VoIP if (i % 3 0) qci EpsBearer::NGBR_VIDEO_TCP_DEFAULT; // 混入一些视频流 EpsBearer bearer (qci); lteHelper-ActivateDataRadioBearer (ueLteDevs.Get (i), ueLteDevs.Get (i), bearer); } // 跑仿真 Simulator::Stop (Seconds (1.0)); Simulator::Run (); Simulator::Destroy (); return 0; }这段代码只搭了一个基本框架。真正要出对比结果还需要用FlowMonitor或者LteStatsCalculator来统计吞吐量、时延、丢包率。LTE模块自带LteStatsCalculator它会生成小区级的RSRP、SINR、吞吐量等统计文件路径一般在lteStats目录下。5.3 指标统计与结果落地统计指标时我强烈建议同时看以下四个维度缺一不可公平性指数Jains Fairness Index衡量用户之间的资源分配公平程度系统平均吞吐量衡量整体效率边缘用户吞吐量第5百分位吞吐量衡量“最差用户”的体验。这是运营商最看重的指标之一丢包率与平均时延衡量调度器对实时业务的支持能力。我实测过一组典型数据15用户、1个基站、30km/h移动速度PF调度比RR在系统吞吐量上提升约35%但公平性指数下降约6%。把流量模型切换成长尾流量也就是少量用户消耗大量流量PF的边缘用户吞吐量比RR低一半以上。对比不同调度器时如果你条件允许一定要跑两组流量模型否则结果很容易被流量特性带偏。6. 仿真中常见的坑与我的排错经验6.1 随机数种子不统一导致的结果失真这是仿真里最隐蔽的坑没有之一。NS-3的随机数系统默认是基于全局的RandomVariableStream如果你跑RR仿真和PFS仿真时没有分别设置相同的随机种子那么用户位置分布、信道衰落过程都不同两个结果根本不在同一个条件下对比得出来的结论毫无意义。正确的做法是每次仿真前固定种子RngSeedManager::SetSeed (7); RngSeedManager::SetRun (42);每个场景单独使用不同的run号而不是不同的seed号这样可以保证在同一个seed下生成互不相关的随机数流。跑对比实验时除了调度器类型以外其他所有随机因素都必须使用同一系列随机数。这个细节决定了你实验结果的可复现性但几乎没人提醒很多论文里的对比结果就是因为种子没控制好导致细微差距其实是随机噪声。6.2 流量模型对调度评价的干扰调度器做的是“资源分配给谁”的决策但它评价的是上层业务的收益。如果流量模型本身不敏感调度器再牛也体现不出来。举一个我踩过的坑由于当时图省事所有用户都配了Full Buffer满缓冲区流量模型发不完的数据随时都在排队跑出来的结果是RR和PF的系统容量几乎一样只差1%不到差点得出“调度算法没用”的错误结论。后来把所有用户都换成ON/OFF模型突发流量PF对RR的优势立刻变成30%以上。原因也不难理解满缓冲区模型意味着每个用户都永远有数据需求调度器不管怎么分配只要总资源不变系统总吞吐量就没有本质区别但在突发流量下好的调度器能够感知谁当前有数据要发、谁的信道刚好变好从而获得调度增益。所以做调度算法对比时至少承担这两类模型一类是高负载的满缓冲区模型用于测试吞吐量上限一类是ON/OFF突发模型用于测试调度器对业务波动的应变能力。缺少任一类型仿真结论的说服力都会打折。6.3 仿真时长与收敛跑多久才算数调度算法的效果不是一个瞬间决定的而是要经过一段时间才能在统计上表现出来。尤其是PF这种带历史平均速率的算法它的收敛时间受平均窗口W影响一般至少要跑3-5倍W的时间才能进入稳态。举例你设置的W是100ms至少要跑300-500ms才能让PF的历史速率统计稳定下来。如果你只跑100msPF还处在“历史统计为空”的初始阶段行为会非常奇怪——有些用户初始T_i为零打分值M_i趋于无穷大调度器会疯狂给这些用户分配资源结果跟稳态行为完全不符。我通常的测试流程是先跑一个短仿真200ms检查系统有没有报错然后跑一个长仿真自定义通常是5-10秒仿真时间同时把统计分成前段、中段、后段三部分分别输出指标。如果三段指标有明显的不一致说明系统还没进入稳态需要继续加长仿真时间。另外还有一个容易忽略的点信道模型的选择会直接影响调度器的表现。如果你的仿真用的是静态信道用户不动、信道不随时间变化PF的“历史平均速率”几乎不起作用因为信道质量一直不变用户的调度优先级也就永远不变。这种场景下PF退化成Max C/I要知道这不是算法本身的问题而是信道模型过于理想化。做调度仿真时建议至少用Jakes衰落模型或3GPP TR 38.901信道模型不然实验结论很难迁移到实际网络中。6.4 调度切换开销不可忽略最后一个值得提醒的坑是调度切换开销。很多算法在理想仿真里表现很好但放到真实场景里会被“用户切换切换开销”拖垮。在NS-3里切换开销默认是关闭的但如果你不自量力地写了一个需要每TTI重算全局最优的调度算法然后跑一个用户频繁切换的小区场景你会发现性能损失主要来自切换信令而不是调度本身。我做DP资源的实验时也遇到类似情况全局DP分配算法在每个调度点都做重排理论增益12%但加上用户切换、RRC重配置的时延后增益只剩不到3%有时候甚至是负的。最后的方案是“快调度慢优化”双层结构PF负责每个TTI的即时调度DP优化器每隔50个TTI才触发一次让优化指令在时间维度上“摊薄”开销。这个思路也适用于你在仿真里跑任何计算量大的调度算法——不要追求无缝全频率优化工程实践中“间隔优化”的性价比永远更高。我在实际项目中越来越深的体会是调度算法根本没有放之四海而皆准的“最优解”只有“适不适合当前约束条件”的折中方案。仿真最大的价值正是在一个可控环境里把这些折中反复验证形成自己的判断力——你最后在频谱效率、公平性、时延、计算复杂度这四个维度之间做的每一个选择都是对网络最基本的理解和尊重。
返回列表