4、速率自适应算法:经典算法与Minstrel实战
速率自适应,说白了就是WiFi设备在动态信道环境下,自动选择最优发送速率的能力。你想想看,信号好的时候用高速率,信号差的时候切低速率,这听起来简单,但实际做起来坑特别多。我这些年调过的WiFi驱动,至少有一半的性能问题都跟速率选择策略有关。
今天咱们就把这块彻底讲透。从经典算法讲起,再到Minstrel的HT/VHT实现细节,最后聊聊我踩过的那些坑。
4.1 经典速率自适应算法
在Minstrel成为主流之前,业界主要有三种思路:ARF、Ondemand,以及一些厂商私有的方案。咱们一个个看。
4.1.1 ARF(Auto Rate Fallback)
ARF是最早的速率自适应算法,1995年就提出来了。它的逻辑非常直白:
- 连续成功发送10帧,就尝试提升一个速率等级
- 连续失败2帧,就立刻降一个速率等级
- 用定时器兜底,防止长时间不触发升降
听起来挺合理对吧?但实际用起来问题很大。我在一个老项目里见过,ARF在弱信号场景下频繁升降,速率像过山车一样抖动。为什么呢?因为它只依赖成功/失败计数,完全不考虑信道质量的变化趋势。
4.1.2 Ondemand算法
Ondemand的思路跟ARF完全不同。它不搞固定阈值,而是按需探测:
- 周期性发送探测帧,测试更高速率是否可行
- 根据探测结果和实际吞吐量,动态调整速率
- 引入“信用”机制,避免频繁探测
Ondemand的好处是减少了ARF那种盲目升降。但我个人觉得,它的探测开销在密集部署场景下是个大问题。我记得有一次在商场AP部署中,Ondemand的探测帧占了将近5%的空口时间,这个代价其实挺高的。
4.1.3 经典算法对比
| 算法 | 核心机制 | 优点 | 缺点 |
|---|---|---|---|
| ARF | 成功/失败计数 | 实现简单,计算开销小 | 滞后严重,抗干扰差 |
| Ondemand | 按需探测+信用机制 | 减少盲目升降 | 探测开销大,响应慢 |
| Minstrel | 多速率采样+统计决策 | 吞吐量最优,适应性强 | 实现复杂,内存开销大 |
4.2 Minstrel算法原理
Minstrel是Linux内核默认的速率自适应算法,也是目前业界公认最成熟的方案。它的核心思想就一句话:用统计代替猜测。
Minstrel维护一个速率表,每个速率都记录:
- 成功概率(probability)
- 吞吐量期望值(expected throughput)
- 最近采样次数
每隔100ms(一个采样周期),Minstrel会做三件事:
- 采样:从速率表中选几个速率发探测帧
- 更新统计:根据探测结果更新成功概率
- 决策:选择期望吞吐量最高的速率作为工作速率
我举个例子你就明白了。假设当前最优速率是MCS7(65Mbps),成功概率90%。但突然来了干扰,连续丢帧。Minstrel不会立刻降速,而是先用MCS5(52Mbps)重传,再不行用MCS3(39Mbps)。这个重传链保证了在信道突变时,数据还能发出去。
4.3 Minstrel HT/VHT实现细节
到了802.11n(HT)和802.11ac(VHT)时代,速率表变得非常庞大。HT有8个MCS,VHT有10个MCS,每个MCS还有不同的带宽(20/40/80MHz)和流数(1/2/3/4流)。
Minstrel怎么处理这个复杂度?
4.3.1 速率分组策略
Minstrel不会对所有速率组合都采样。它把速率按“吞吐量等级”分组:
- 每组选一个代表速率进行采样
- 组内其他速率通过插值估算成功概率
- 每4个采样周期,重新评估分组是否合理
这个设计很巧妙。我记得在调试一个802.11ac 3x3的网卡时,速率组合有30多种。如果不分组,每个周期光采样就要发几十帧,空口开销根本扛不住。
4.3.2 采样窗口与EWMA
Minstrel用指数加权移动平均(EWMA)来平滑成功概率:
new_prob = old_prob * (1 - alpha) + sample_result * alpha
alpha值默认是0.25。这个值我调过很多次:
- alpha太大(如0.5):对信道变化敏感,但容易抖动
- alpha太小(如0.1):稳定,但响应慢
我个人习惯在移动场景下把alpha调到0.3,固定部署用0.2。没有绝对最优,得看实际环境。
4.3.3 多流与带宽选择
VHT下Minstrel还要考虑流数和带宽。它的策略是:
- 优先尝试更高流数(如2流→3流)
- 带宽提升只在信号质量好时尝试(如20MHz→40MHz)
- 如果高流数/高带宽的成功概率低于50%,自动回退
4.4 实际部署中的坑
这部分是我最想分享的。Minstrel虽然成熟,但部署起来问题真不少。
4.4.1 采样帧的“虚假成功”
Minstrel用探测帧来采样,但探测帧通常很短。短帧在信道中的传输成功率,跟长帧完全不是一回事。我曾经遇到一个案例:采样帧成功率95%,但实际数据帧(1500字节)成功率只有60%。
为什么?因为长帧更容易被干扰打断。Minstrel默认没考虑帧长对成功率的影响,这是个坑。
解决办法:在驱动层把采样帧长度模拟成实际数据帧长度,或者对采样结果加一个帧长修正因子。
4.4.2 多用户场景下的“饿死”问题
Minstrel是为单用户设计的。在多用户场景下,如果某个STA的信号很差,Minstrel会一直用低速率发,占用大量空口时间。其他高速率STA反而被“饿死”。
我记得在一个体育馆部署中,就因为这个原因,整体吞吐量下降了40%。
解决办法:引入“公平性因子”,当某个STA的速率低于阈值时,限制它的发送机会。
4.4.3 速率表初始化
Minstrel刚启动时,速率表是空的。它需要先采样才能决策。这个初始化阶段如果处理不好,会导致前几秒的速率很低。
我曾经在快速漫游场景中吃过这个亏。STA每10秒切换一次AP,每次切换都要重新初始化速率表,导致用户体验极差。
解决办法:把速率表状态保存在驱动层,AP切换时直接恢复,不用重新采样。
4.4.4 硬件offload的兼容性
很多新网卡把速率选择offload到硬件。但硬件厂商的实现跟Minstrel不完全一致。我遇到过某款芯片,硬件速率选择完全不考虑重传链,导致弱信号下丢包率飙升。
解决办法:如果硬件offload效果不好,就在驱动层强制使用软件Minstrel,或者对硬件速率选择结果做二次修正。
4.5 Minstrel核心流程
下面这张图展示了Minstrel在一个采样周期内的完整决策流程:
这个流程看起来简单,但每个步骤都有调优空间。比如采样速率的选择策略,是优先选从未采样的,还是优先选低采样次数的?我做过对比测试,优先选从未采样的在信道稳定时表现更好,但信道变化快时容易错过最优速率。
4.6 小结
速率自适应算法,说白了就是在“探索”和“利用”之间找平衡。ARF太保守,Ondemand太激进,Minstrel用统计方法找到了一个不错的中间点。但Minstrel也不是万能的,采样帧虚假成功、多用户饿死、初始化慢、硬件offload兼容性,这些都是实际部署中必须解决的问题。
嗯,如果你正在调WiFi性能,我建议先从Minstrel的统计表入手,看看每个速率的成功概率和采样次数。很多时候,问题一眼就能看出来——比如某个速率采样次数为0,或者成功概率异常高/低。这些数据不会骗人。