4、速率自适应算法:经典算法与Minstrel实战

速率自适应,说白了就是WiFi设备在动态信道环境下,自动选择最优发送速率的能力。你想想看,信号好的时候用高速率,信号差的时候切低速率,这听起来简单,但实际做起来坑特别多。我这些年调过的WiFi驱动,至少有一半的性能问题都跟速率选择策略有关。

今天咱们就把这块彻底讲透。从经典算法讲起,再到Minstrel的HT/VHT实现细节,最后聊聊我踩过的那些坑。

4.1 经典速率自适应算法

在Minstrel成为主流之前,业界主要有三种思路:ARF、Ondemand,以及一些厂商私有的方案。咱们一个个看。

4.1.1 ARF(Auto Rate Fallback)

ARF是最早的速率自适应算法,1995年就提出来了。它的逻辑非常直白:

  • 连续成功发送10帧,就尝试提升一个速率等级
  • 连续失败2帧,就立刻降一个速率等级
  • 用定时器兜底,防止长时间不触发升降

听起来挺合理对吧?但实际用起来问题很大。我在一个老项目里见过,ARF在弱信号场景下频繁升降,速率像过山车一样抖动。为什么呢?因为它只依赖成功/失败计数,完全不考虑信道质量的变化趋势。

ARF的致命缺陷: 它对短时突发干扰极度敏感。一次突发碰撞导致连续丢帧,速率立刻下降;干扰消失后又要等10次成功才能升回去。这个滞后效应在802.11n/ac时代尤其致命。

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会做三件事:

  1. 采样:从速率表中选几个速率发探测帧
  2. 更新统计:根据探测结果更新成功概率
  3. 决策:选择期望吞吐量最高的速率作为工作速率
关键点: 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还要考虑流数和带宽。它的策略是:

  1. 优先尝试更高流数(如2流→3流)
  2. 带宽提升只在信号质量好时尝试(如20MHz→40MHz)
  3. 如果高流数/高带宽的成功概率低于50%,自动回退
经验之谈: 80MHz带宽在真实环境中很少能跑满。我测过很多场景,80MHz的实际吞吐量只比40MHz高10-20%,但功耗和干扰却大得多。如果你的设备对功耗敏感,建议限制最大带宽为40MHz。

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,或者对硬件速率选择结果做二次修正。

避坑指南: 我曾经在一个项目中,因为没检查硬件offload的速率选择逻辑,导致线上问题排查了整整两周。最后发现是硬件在低信号下选了MCS7,但重传链只有MCS7和MCS0,中间缺了MCS3/4/5。这个教训让我养成了一个习惯:上线前必须用抓包工具验证速率选择行为。

4.5 Minstrel核心流程

下面这张图展示了Minstrel在一个采样周期内的完整决策流程:

Minstrel单周期决策流程 周期开始 (100ms) 步骤1:选择采样速率 从未采样/低采样次数的速率中选1-2个 步骤2:发送探测帧 用采样速率发送4-10个探测帧 步骤3:更新统计表 EWMA更新成功概率和期望吞吐量 步骤4:选择工作速率 选期望吞吐量最高的速率作为主速率 步骤5:构建重传链 主速率→次优速率→...→基础速率 周期结束,等待下一周期 关键参数 • 采样周期:100ms • EWMA alpha:0.25 • 探测帧数:4-10 • 重传链长度:4级

这个流程看起来简单,但每个步骤都有调优空间。比如采样速率的选择策略,是优先选从未采样的,还是优先选低采样次数的?我做过对比测试,优先选从未采样的在信道稳定时表现更好,但信道变化快时容易错过最优速率。

4.6 小结

速率自适应算法,说白了就是在“探索”和“利用”之间找平衡。ARF太保守,Ondemand太激进,Minstrel用统计方法找到了一个不错的中间点。但Minstrel也不是万能的,采样帧虚假成功、多用户饿死、初始化慢、硬件offload兼容性,这些都是实际部署中必须解决的问题。

嗯,如果你正在调WiFi性能,我建议先从Minstrel的统计表入手,看看每个速率的成功概率和采样次数。很多时候,问题一眼就能看出来——比如某个速率采样次数为0,或者成功概率异常高/低。这些数据不会骗人。