Linux AQM 深度剖析: 拥塞控制

Linux AQM 深度剖析: 拥塞控制

引言: 为什么我们需要AQM?

想象一下高峰时段的高速公路收费站, 当车辆到达速度超过处理能力时, 车辆开始排队. 传统做法是等队列排满后直接拒绝后来的新车辆(尾丢弃), 这将导致所有新来的车都被挡在外面, 不管它们有多紧急. 早期网络设备正是采用这种 "尾丢弃" 策略. 1988年的 "拥塞崩溃" 事件让研究者意识到, 被动等待队列溢出再丢包, 会导致全局同步、高延迟、低吞吐等问题. Active Queue Management(主动队列管理)应运而生, 它的核心理念是: 在队列真正满之前就主动、智能地丢包或标记, 向发送端提前发出拥塞信号, 从而尽可能避免拥塞崩溃场景的产生

一、AQM核心思想与设计哲学

1.1 从被动到主动的范式转变

**传统尾丢弃(Tail Drop)的问题: **

  1. 锁死效应: 队列满后, 所有新包都被丢弃, 无论其重要性
  2. 全局同步: 多个TCP连接同时超时重传, 造成流量震荡
  3. Bufferbloat: 过大的缓冲区掩盖了拥塞信号, 导致RTT激增

**AQM的设计目标: **

  • 低延迟: 保持较小的队列长度
  • 高吞吐: 充分利用链路带宽
  • 公平性: 不同流之间公平分享带宽
  • 稳定性: 避免队列长度剧烈波动

1.2 AQM的三种控制范式

AQM控制范式 基于队列长度 基于排队延迟 基于速率估算 RED/Random Early Detection GRED/Generalized RED CoDel/Controlled Delay PIE/PROPORTIONAL INTEGRAL ENHANCED CAKE/Common Applications Kept Enhanced

表1: 三种AQM范式的对比

范式 核心指标 优点 缺点 适用场景
队列长度 包数量 实现简单, 计算开销小 对突发流量敏感, 需调参 传统网络设备
排队延迟 时间间隔 自适应链路速率, 抵抗Bufferbloat 需要高精度时间戳 家庭网关, 无线网络
速率估算 到达/服务速率 最接近真实拥塞状态 实现复杂, 计算量大 异构网络, 混合流量

二、Linux AQM实现架构

2.1 Linux流量控制子系统(TC)概览

Linux的AQM实现在网络协议栈的排队层 , 主要通过tc(traffic control)框架提供. 整个架构是一个多层的队列规则(qdisc)系统

c 复制代码
/* 核心数据结构: 每个网络接口的队列规则 */
struct net_device {
    // ...
    struct Qdisc *qdisc;          // 根qdisc
    struct Qdisc *qdisc_sleeping; // 默认qdisc
    // ...
};

/* qdisc基础结构(简化版) */
struct Qdisc {
    int             (*enqueue)(struct sk_buff *skb, struct Qdisc *sch);
    struct sk_buff *(*dequeue)(struct Qdisc *sch);
    unsigned int    (*drop)(struct Qdisc *sch);
    // ... 统计信息、参数等
};

2.2 AQM在协议栈中的位置

graph TB subgraph "Linux网络协议栈" A[应用程序] --> B[Socket层] B --> C[TCP/UDP层] C --> D[IP层] D --> E[Netfilter钩子] E --> F[流量控制层
(Qdisc/AQM在这里)] F --> G[网络设备队列
(Driver Level)] G --> H[物理网卡] end subgraph "TC框架组件" F1[Classful Qdisc
e.g., HTB, CBQ] F2[Classless Qdisc
e.g., RED, CoDel] F3[过滤器(Filter)
分类流量] F4[动作(Action)
丢包、标记、重排队] F --> F1 F --> F2 F3 --> F1 F3 --> F2 F1 --> F4 F2 --> F4 end

三、经典算法深度解析

3.1 RED(Random Early Detection)算法

生活比喻: 像一个聪明的电影院经理. 当候影厅人数达到一定数量时(但还没满), 经理开始随机建议一些观众改看下一场(丢包). 这样避免了候影厅完全爆满时所有人都进不去的混乱局面

3.1.1 RED核心算法原理

RED维护一个指数加权移动平均(EWMA) 的队列长度:

复制代码
avg = (1 - w_q) * avg + w_q * current_qlen

其中w_q是权重因子, 决定了对瞬时队列长度的敏感度

丢包概率曲线:

复制代码
        ^
        | 丢包概率
   max_p|           /¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        |          /
        |         /
        |        /
        |       /
        |      /
       0|_____/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯>
         min_th    max_th    队列长度
3.1.2 Linux RED实现关键代码
c 复制代码
/* Linux内核中RED的核心参数结构 */
struct red_parms {
    /* 配置参数 */
    u32 qth_min;    /* 最小阈值(字节/包数) */
    u32 qth_max;    /* 最大阈值 */
    u32 Scell_max;  /* 最大空闲时间 */
    u32 max_P;      /* 最大丢包概率, 固定点表示 */
    u32 max_P_reciprocal; /* max_P的倒数, 用于快速计算 */
    u32 qth_delta;  /* max_th - min_th */
    
    /* 动态状态 */
    u32 qave;       /* 平均队列长度(EWMA) */
    u32 qcount;     /* 上次丢包后的入队包数 */
    unsigned long qR; /* 随机数种子 */
};

/* RED的核心决策函数 */
static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
    struct red_sched_data *q = qdisc_priv(sch);
    
    /* 计算当前平均队列长度 */
    q->vars.qavg = red_calc_qavg(&q->parms, &q->vars, sch->q.qlen);
    
    /* 检查是否在阈值范围内 */
    if (red_is_idling(&q->vars))
        red_end_of_idle_period(&q->vars);
    
    /* 根据平均队列长度决定丢包概率 */
    if (q->vars.qavg <= q->parms.qth_min) {
        /* 队列短, 不丢包 */
        q->vars.qcount = -1;
        enqueue:
        return qdisc_enqueue_tail(skb, sch);
    } else if (q->vars.qavg >= q->parms.qth_max) {
        /* 队列过长, 强制丢包 */
        q->vars.qcount = -1;
        return qdisc_drop(skb, sch, NULL);
    } else {
        /* 在min_th和max_th之间, 随机丢包 */
        if (++q->vars.qcount == 0)
            red_random(&q->vars.qR); /* 更新随机数 */
        
        /* 计算丢包概率 */
        pb = red_calc_p(&q->parms, q->vars.qavg);
        
        /* 基于计数器的丢包概率调整 */
        if (red_mark_probability(&q->parms, &q->vars, pb)) {
            q->vars.qcount = 0;
            q->vars.forced_mark = red_ecn_mark(sch); /* 尝试ECN标记而非丢包 */
            if (q->vars.forced_mark)
                goto enqueue; /* ECN标记, 仍然入队 */
            else
                return qdisc_drop(skb, sch, NULL); /* 丢包 */
        }
        goto enqueue;
    }
}
3.1.3 RED状态机

qavg < min_th min_th ≤ qavg < max_th qavg ≥ max_th 入队所有包 以概率p丢包 继续处理 丢包率100% 缓解拥塞后恢复 正常状态 检查队列长度 队列短 队列中 队列长 随机丢包 强制丢包

3.2 CoDel(Controlled Delay)算法

生活比喻: 像一位经验丰富的咖啡师. 她不关心排队人数多少, 只关注每个顾客的等待时间. 一旦发现某个顾客等得太久(比如超过5分钟), 她就加快制作速度或暂时停止接新订单

3.2.1 CoDel的核心洞察

CoDel的创始人Kathleen Nichols和Van Jacobson认识到: 排队延迟(而非队列长度)才是衡量拥塞的最佳指标. 因为:

  1. 延迟直接影响应用性能
  2. 延迟与链路速率无关, 适应性更强
  3. 延迟能更早检测到拥塞
3.2.2 CoDel算法实现
c 复制代码
/* CoDel控制参数 */
struct codel_params {
    u32 target;      /* 目标排队延迟(默认5ms) */
    u32 interval;    /* 监控窗口宽度(默认100ms) */
    u32 ecn;         /* 是否启用ECN标记 */
};

/* CoDel状态变量 */
struct codel_vars {
    u32 count;              /* 丢弃计数 */
    u32 lastcount;          /* 上次进入丢包状态的count值 */
    bool dropping;          /* 是否处于丢包状态 */
    u32 rec_inv_sqrt;       /* 1/sqrt(count)的倒数, 用于计算丢包间隔 */
    codel_time_t first_above_time; /* 首次超过目标延迟的时间 */
    codel_time_t drop_next; /* 下次丢包的时间点 */
    codel_time_t ldelay;    /* 观察到的排队延迟 */
};

/* CoDel入队逻辑简化版 */
static int codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
    struct codel_sched_data *q = qdisc_priv(sch);
    
    /* 入队 */
    bool dropped = false;
    dropped = codel_should_drop(skb, sch, &q->params, &q->vars,
                                 q->stats.backlog, skb->len);
    
    if (dropped) {
        if (q->params.ecn && INET_ECN_set_ce(skb))
            goto enqueue; /* ECN标记, 不丢包 */
        qdisc_drop(skb, sch, NULL);
        return NET_XMIT_DROP;
    }
    
enqueue:
    return qdisc_enqueue_tail(skb, sch);
}

/* CoDel的核心决策函数 */
bool codel_should_drop(struct sk_buff *skb,
                       struct Qdisc *sch,
                       struct codel_params *params,
                       struct codel_vars *vars,
                       u32 backlog,
                       u32 skb_len)
{
    codel_time_t now = codel_get_time();
    u32 sojourn_time = now - codel_get_enqueue_time(skb);
    
    /* 计算瞬时排队延迟 */
    vars->ldelay = sojourn_time;
    
    /* 判断是否超过目标延迟 */
    if (sojourn_time < params->target || backlog <= 2*skb_len) {
        /* 延迟可接受, 重置状态 */
        vars->first_above_time = 0;
        return false;
    }
    
    /* 延迟过高, 开始计时 */
    if (vars->first_above_time == 0)
        vars->first_above_time = now + params->interval;
    else if (now >= vars->first_above_time) {
        /* 持续高延迟超过一个interval, 进入丢包状态 */
        vars->dropping = true;
        vars->first_above_time = 0;
    }
    
    if (!vars->dropping)
        return false;
    
    /* 计算丢包间隔: interval / sqrt(count) */
    if (now >= vars->drop_next) {
        vars->count++; /* 增加丢包计数 */
        
        /* 计算下次丢包时间 */
        if (!vars->count)
            vars->count--;
        vars->drop_next = codel_control_law(now, params->interval,
                                            vars->rec_inv_sqrt);
        
        return true; /* 这次丢包 */
    }
    
    return false;
}
3.2.3 CoDel状态转换图

初始化 每个包到达时 sojourn_time < target sojourn_time ≥ target 继续入队 记录first_above_time 超时前仍高延迟 超时前延迟恢复 进入丢包模式 drop_next到达时 更新drop_next 继续丢包周期 队列为空或延迟正常 重置状态 正常状态 测量延迟 延迟正常 延迟偏高 开始计时 持续高延迟 恢复正常 丢包状态 丢包 计算下次丢包 退出丢包

3.3 PIE(Proportional Integral controller Enhanced)

生活比喻: 像一个智能恒温空调系统. 它不仅测量当前温度(当前延迟), 还考虑温度变化趋势(延迟变化率), 然后比例-积分地调节制冷功率(丢包率), 使温度稳定在设定值

3.3.1 PIE的控制理论基础

PIE使用经典的PI控制器(比例-积分控制器):

复制代码
丢包概率 = α * (当前延迟 - 目标延迟) + β * (延迟累积误差)

其中:

  • α: 比例系数, 快速响应当前偏差
  • β: 积分系数, 消除稳态误差
3.3.2 PIE算法核心
c 复制代码
/* PIE状态结构 */
struct pie_params {
    psched_time_t target;        /* 目标延迟(默认15ms) */
    psched_time_t tupdate;        /* 更新间隔(默认15ms) */
    u32 alpha;                   /* 比例系数α */
    u32 beta;                    /* 积分系数β */
    u32 max_burst;               /* 最大突发容忍量 */
    u32 ecn;                     /* 启用ECN */
    u32 bytemode;                /* 基于字节计数 */
};

struct pie_vars {
    psched_time_t qdelay;        /* 当前排队延迟 */
    psched_time_t qdelay_old;    /* 上次的排队延迟 */
    u64 burst_allowance;         /* 剩余突发配额 */
    psched_time_t burst_time;    /* 突发开始时间 */
    u32 dropping;                /* 是否处于丢包状态 */
    u32 prob;                    /* 当前丢包概率 */
    u32 accu_prob;               /* 累积丢包概率 */
    u32 dq_count;                /* 出队计数器 */
    psched_time_t dq_tstamp;     /* 上次出队时间 */
};

/* PIE的概率计算函数 */
static void pie_calculate_probability(struct pie_params *params,
                                      struct pie_vars *vars)
{
    psched_time_t qdelay = vars->qdelay;  /* 当前延迟 */
    psched_time_t qdelay_old = vars->qdelay_old; /* 旧延迟 */
    u32 prob = vars->prob;                /* 当前概率 */
    
    /* 比例项: 当前偏差 */
    u32 p = (u32)((qdelay - params->target) * params->alpha);
    
    /* 积分项: 历史累积 */
    p += (u32)((qdelay - qdelay_old) * params->beta);
    
    /* 限制概率范围 */
    p = min(p, MAX_PROB);
    
    /* 平滑更新 */
    prob += (p - prob) >> 4;
    
    vars->prob = prob;
    vars->qdelay_old = qdelay;
}
3.3.3 PIE的突发容忍机制

PIE引入burst_allowance概念, 允许短时间内的流量突发不触发丢包:

c 复制代码
static bool pie_should_drop(struct sk_buff *skb,
                            struct pie_params *params,
                            struct pie_vars *vars)
{
    /* 检查突发配额 */
    if (vars->burst_allowance > 0) {
        if (params->bytemode)
            vars->burst_allowance -= skb->len;
        else
            vars->burst_allowance--;
        
        /* 突发期间不丢包 */
        if (vars->burst_allowance > 0)
            return false;
    }
    
    /* 正常丢包决策 */
    u32 rand = prandom_u32_max(MAX_PROB);
    if (rand < vars->prob) {
        /* 命中丢包 */
        if (!vars->burst_allowance)
            vars->burst_allowance = params->max_burst;
        
        return true;
    }
    
    return false;
}

四、Linux AQM配置与实战

4.1 使用tc命令配置AQM

bash 复制代码
# 1. 查看当前qdisc配置
tc qdisc show dev eth0

# 2. 配置RED队列(经典配置)
tc qdisc add dev eth0 parent 1:1 handle 10: red \
    limit 600KB min 30KB max 150KB burst 20 \
    avpkt 1KB bandwidth 100Mbit probability 0.1

# 3. 配置CoDel(现代推荐)
tc qdisc add dev eth0 root codel \
    limit 1000 target 5ms interval 100ms ecn

# 4. 配置PIE
tc qdisc add dev eth0 root pie \
    limit 1000 target 15ms tupdate 15ms alpha 2 beta 20 ecn

# 5. 结合分类器使用(HTB + CoDel)
tc qdisc add dev eth0 root handle 1: htb default 30
tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 60mbit
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 40mbit

tc qdisc add dev eth0 parent 1:10 handle 10: codel ecn
tc qdisc add dev eth0 parent 1:20 handle 20: fq_codel

4.2 简单实例: 构建一个AQM测试环境

bash 复制代码
#!/bin/bash
# aqm_demo.sh - 简单的AQM演示脚本

# 设置网络命名空间(避免影响主机)
ip netns add aqm_test
ip link add veth0 type veth peer name veth1 netns aqm_test

# 配置主机端
ip addr add 10.0.0.1/24 dev veth0
ip link set veth0 up

# 配置命名空间端
ip netns exec aqm_test ip addr add 10.0.0.2/24 dev veth1
ip netns exec aqm_test ip link set veth1 up
ip netns exec aqm_test ip link set lo up

# 在命名空间中应用CoDel AQM
ip netns exec aqm_test tc qdisc add dev veth1 root codel \
    limit 500 target 5ms interval 100ms ecn

# 启动iperf3服务器(在命名空间中)
ip netns exec aqm_test iperf3 -s -D

# 在主机端运行iperf3客户端, 观察AQM效果
iperf3 -c 10.0.0.2 -t 30 -P 4

# 监控队列统计信息
watch -n 1 'ip netns exec aqm_test tc -s qdisc show dev veth1'

# 清理
ip netns del aqm_test

4.3 核心监控与调试命令

bash 复制代码
# 1. 实时监控队列统计
watch -n 0.5 'tc -s qdisc show dev eth0'

# 2. 详细统计信息(CoDel示例)
tc -s -d qdisc show dev eth0
# 输出示例: 
# qdisc codel 8001: root refcnt 2 limit 1000p target 5.0ms interval 100.0ms ecn 
#  Sent 123456789 bytes 98765 pkt (dropped 123, overlimits 0 requeues 0) 
#  backlog 0b 0p requeues 0 
#   count 123 lastcount 10 ldelay 4.0ms drop_next 0us 
#   maxpacket 1514 ecn_mark 12345 drop_overlimit 0

# 3. 网络延迟测量(发现Bufferbloat)
ping -A 8.8.8.8  # 使用自适应ping, 观察RTT变化

# 4. 使用tcpdump观察ECN标记
tcpdump -i eth0 'ip[1] & 0x03 != 0'  # 捕获ECN位设置的包

# 5. 内核日志中的AQM信息
dmesg | grep -i "codel\|red\|pie\|aqm"

# 6. 系统级网络统计
nstat -az | grep -E "TcpExtTCPECN|TcpExtECN"

4.4 AQM调优指南

表2: 常见场景AQM算法选择

场景 推荐算法 关键参数 调优建议
家庭宽带 fq_codel target=5ms, interval=100ms 启用ECN, limit根据内存调整
数据中心 CoDel target=1ms, interval=10ms 低延迟优先, 监控丢包率
无线网络 PIE target=15ms, tupdate=15ms 增大目标延迟容忍突发
传统路由器 RED min=5%, max=30%, maxp=0.1 需根据带宽精细调参
混合流量 CAKE bandwidth=上下行速率 自动适应, 配置简单

**调优步骤: **

  1. 基线测量: 无AQM时的延迟和吞吐
  2. 初始配置: 使用算法默认值
  3. 压力测试: 模拟真实流量模式
  4. 监控指标: 延迟分布、丢包率、吞吐量
  5. 迭代优化: 微调参数, 寻找最佳平衡点

五、现代AQM发展趋势

5.1 FQ-CoDel: 公平队列与CoDel的结合

FQ-CoDel(Fair Queuing with Controlled Delay)是目前Linux默认的qdisc, 结合了:

  1. 流间公平: 每个流有独立队列
  2. 流内调度: CoDel管理每个流的延迟
  3. 抗DoS: 新流获得有限配额, 防止饥饿
c 复制代码
/* FQ-CoDel流结构 */
struct fq_codel_flow {
    struct sk_buff *head;      /* 流队列头 */
    struct sk_buff *tail;      /* 流队列尾 */
    struct list_head flowchain; /* 全局流链表 */
    u32 deficit;               /* 当前赤字(调度权重) */
    u32 dropped;               /* 该流丢包计数 */
    struct codel_vars cvars;   /* CoDel状态 */
};

/* FQ-CoDel主结构 */
struct fq_codel_sched_data {
    struct fq_codel_flow *flows;   /* 流数组 */
    struct list_head new_flows;    /* 新流列表 */
    struct list_head old_flows;    /* 旧流列表 */
    u32 quantum;                   /* 每次调度字节数 */
    u32 drop_batch_size;           /* 批量丢包大小 */
    u32 memory_limit;              /* 内存限制 */
    u32 flow_quantum;              /* 每流quantum */
    struct codel_params cparams;   /* CoDel参数 */
};

5.2 CAKE: 综合解决方案

CAKE(Common Applications Kept Enhanced)集成了:

  • 整形和调度: HTB-like整形 + DRR调度
  • AQM: CoDel-derivative算法
  • 分类: 自动流分类(src/dst, host, flow)
  • NAT友好: 保留NAT后的公平性
bash 复制代码
# CAKE的简单配置
tc qdisc add dev eth0 root cake bandwidth 100Mbit besteffort dual-dsthost nat

# 关键特性: 
# 1. 自动带宽检测
# 2. 每主机公平性(即使经过NAT)
# 3. 低开销的优先级处理
# 4. 集成AQM(基于CoDel改进)

六、深度调试与性能分析

6.1 内核tracepoint

bash 复制代码
# 启用qdisc事件跟踪
echo 1 > /sys/kernel/debug/tracing/events/qdisc/enable

# 查看特定qdisc的跟踪
cat /sys/kernel/debug/tracing/trace_pipe | grep "codel\|red"

# 跟踪包在qdisc中的生命周期
perf record -e 'qdisc:*' -a sleep 10
perf script

6.2 性能指标监控

表3: 关键性能指标与监控方法

指标 意义 监控命令 健康范围
排队延迟 包在队列中等待时间 tc -s qdisc < target_ms
丢包率 AQM主动丢包比例 tc -s qdisc 0.1%-5%
ECN标记率 拥塞标记比例 nstat 与丢包率相当
队列长度 瞬时队列占用 tc -s qdisc 波动的, 不应饱和
吞吐量 实际传输速率 iftop 接近带宽

6.3 常见问题排查

问题1: AQM似乎没有生效

bash 复制代码
# 检查内核模块
lsmod | grep sch_codel

# 检查qdisc是否真的附加
tc qdisc show dev eth0

# 检查计数器是否增加
watch -n 1 'tc -s qdisc show dev eth0 | grep dropped'

问题2: 延迟仍然很高

bash 复制代码
# 确认是排队延迟还是其他延迟
ping -A target_host

# 检查Bufferbloat
# 使用flent工具进行波形测试
flent rrul -H server_ip -l 60 --step-size=.05

# 调整AQM参数
tc qdisc change dev eth0 root codel target 2ms interval 50ms

问题3: 吞吐量下降太多

bash 复制代码
# 检查是否过度丢包
tc -s qdisc show dev eth0 | grep -A5 "drop"

# 调整目标延迟
# 增加target值可以降低丢包率, 但增加延迟
tc qdisc change dev eth0 root codel target 10ms

# 或者尝试PIE(对吞吐更友好)
tc qdisc replace dev eth0 root pie

七、架构总结与未来展望

7.1 Linux AQM架构全景图

硬件/驱动层 内核空间 Classful Qdiscs Classless AQM Qdiscs 底层机制 用户空间工具 网卡队列 硬件时间戳 Offload能力 Netlink API Qdisc框架 包调度器 定时器子系统 随机数生成 ECN支持 RED/PARED CoDel/FQ_CoDel PIE CAKE SFQ/FQ HTB CBQ PRIO tc命令 ip命令 其他配置工具

7.2 各算法综合对比

表4: Linux主流AQM算法全面对比

特性 RED CoDel PIE FQ-CoDel CAKE
控制目标 队列长度 排队延迟 排队延迟 延迟+公平 综合QoS
需调参数 多(4+) 少(2-3) 中(3-4) 少(2-3) 少(1-2)
自适应性 很高
抗突发 很好
公平性 流级公平 主机级公平
计算开销 中高
默认推荐 特定场景 Linux默认 复杂场景
ECN支持
部署难度 很低

八、结语

Linux AQM的发展历程, 是从简单粗暴的尾丢弃, 到基于队列长度的RED, 再到基于延迟的CoDel和PIE, 最终演变为综合解决方案FQ-CoDel和CAKE. 这个演进过程体现了网络拥塞控制思想的深化: 从只看局部状态到全局优化, 从被动反应到主动预防, 从单一指标到多目标平衡

核心启示:

  1. 延迟比队列长度更重要: 这是CoDel带给我们的最重要的洞见
  2. 公平性必须显式处理: FQ-CoDel证明了流级别的公平调度是可行的
  3. 简单性至关重要: 好的AQM应该几乎不需要调参
  4. 测量优于猜测: 基于实际延迟测量, 而非预设模型

在实际部署中, 对于大多数Linux系统, FQ-CoDel是推荐的默认选择, 它提供了良好的开箱即用体验. 对于特定场景(如无线网络), 可以考虑PIE;对于复杂网络环境(如家庭网关), CAKE可能是更好的选择

相关推荐
艾醒2 小时前
大模型原理剖析——突破LLM效率瓶颈:多标记预测(MTP)技术深度解析与实战
算法
智驱力人工智能2 小时前
森林防火无人机火焰监测系统 构建“天空地”一体化智能防火体系 无人机火焰检测,支持红色火焰检测 城市高层建筑无人机火焰识别
人工智能·深度学习·opencv·算法·目标检测·无人机·边缘计算
多米Domi0112 小时前
0x3f第12天 0-1背包
python·算法·leetcode·动态规划
renke33642 小时前
Flutter 2025 状态管理工程体系:从简单共享到复杂协同,构建可预测、可测试、可维护的状态流架构
flutter·架构
Bruce_kaizy2 小时前
c++图论————最短路之Floyd&Dijkstra算法
c++·算法·图论
徐小夕2 小时前
pxcharts 多维表格开源!一款专为开发者和数据分析师打造的轻量化智能表格
前端·架构·github
lifejump2 小时前
Pikachu | SSRF
服务器·web安全·安全性测试
WBluuue2 小时前
AtCoder Beginner Contest 437(ABCDEF)
c++·算法
郝学胜-神的一滴2 小时前
Linux 下循环创建多线程:深入解析与实践指南
linux·服务器·c++·程序人生·算法·设计模式