第4章:多核编程模型概述

各位同学,今天我们来聊聊多核编程模型。说实话,这个话题我讲了十几年,每次都有新感悟。你想想看,网络处理器从单核发展到几十核甚至上百核,编程模型就是连接硬件和应用的桥梁。选对了,事半功倍;选错了,后面全是坑。

我个人习惯把多核编程模型分成三大类:Run-to-Completion、Pipeline 和 Hybrid。咱们一个一个来看。

4.1 Run-to-Completion 模型

这个模型最直观。每个核独立处理一个完整的数据包。从收包、解析、查表、修改到转发,全在一个核上完成。说白了,就是「谁接的活谁干完」。

核心思想:每个数据包由单个核从头处理到尾,不中断、不切换。

我在项目中遇到过这样一个场景:早期做家用路由器,CPU只有两个核。用RTC模型,代码写起来特别爽。每个核跑一个独立的收包线程,互不干扰。代码结构清晰,调试也方便。

// RTC 模型伪代码示例
void core_main(int core_id) {
    while (1) {
        packet_t *pkt = rx_dequeue(core_id);  // 从本核队列收包
        if (pkt == NULL) continue;
        
        parse_header(pkt);                    // 解析
        lookup_route(pkt);                    // 查表
        modify_packet(pkt);                   // 修改
        tx_enqueue(pkt);                      // 转发
    }
}

优点:

  • 实现简单,没有核间通信开销
  • 数据局部性好,cache命中率高
  • 调试方便,问题容易定位

缺点:

  • 负载均衡难做。有的包处理快,有的包处理慢
  • 扩展性受限。核多了以后,收包队列成为瓶颈
  • 功能耦合度高,不好做模块化

注意:我曾经在一个16核的NPU上硬套RTC模型,结果发现8个核忙死,8个核闲死。负载不均的问题在核数超过4个时就会明显暴露。

4.2 Pipeline 模型

这个模型借鉴了工业流水线的思想。每个核只做一件事,做完传给下一个核。比如核0专门收包,核1专门解析,核2专门查表,核3专门转发。

为什么会有人用这个模型?因为网络处理本身就是流水线式的。你想想看,收包、解析、查表、修改、转发,天然就是串行的步骤。

// Pipeline 模型伪代码示例
void stage_parse(packet_t *pkt) {
    parse_header(pkt);
    send_to_next_stage(pkt, STAGE_LOOKUP);
}

void stage_lookup(packet_t *pkt) {
    lookup_route(pkt);
    send_to_next_stage(pkt, STAGE_MODIFY);
}

void stage_modify(packet_t *pkt) {
    modify_packet(pkt);
    send_to_next_stage(pkt, STAGE_TX);
}

优点:

  • 每个核的代码简单,功能单一
  • 容易做深度优化,比如查表核可以专门优化查表算法
  • 扩展性好,加核就是加流水线深度

缺点:

  • 核间通信开销大,每个包都要传递多次
  • 流水线平衡难做。有的阶段快,有的阶段慢
  • 某个核挂了,整条流水线都停

经验之谈:我做过一个40Gbps的转发设备,用了Pipeline模型。最难的不是写代码,而是调流水线平衡。最后不得不在慢的阶段加多个核并行,快的阶段合并功能。嗯,这里要注意,流水线深度不是越深越好,4-6级是比较合理的范围。

4.3 Hybrid 模型

说白了,就是把RTC和Pipeline结合起来。实际项目中,纯RTC或纯Pipeline都很少见。大家都会根据业务特点做混合。

我常用的混合方式是:入口用RTC,内部处理用Pipeline。比如收包阶段每个核独立收包,然后根据包类型分发到不同的处理流水线。

// Hybrid 模型伪代码示例
void core_main(int core_id) {
    while (1) {
        packet_t *pkt = rx_dequeue(core_id);
        if (pkt == NULL) continue;
        
        // 快速路径:RTC处理
        if (is_fast_path(pkt)) {
            process_fast(pkt);
            tx_enqueue(pkt);
            continue;
        }
        
        // 慢速路径:送入Pipeline
        dispatch_to_pipeline(pkt, get_pkt_type(pkt));
    }
}

优点:

  • 灵活,可以根据业务特点定制
  • 兼顾性能和模块化
  • 容易做差异化处理

缺点:

  • 设计复杂,需要深入理解业务
  • 调试困难,问题可能跨多个模型
  • 维护成本高

4.4 三种模型对比

维度 RTC Pipeline Hybrid
实现难度
性能上限 最高
扩展性
负载均衡 灵活
调试难度
适用场景 小规模、简单业务 大规模、固定流程 复杂业务、混合流量

我的建议:新手先从RTC入手,跑通后再考虑优化。等遇到性能瓶颈了,再逐步引入Pipeline或Hybrid。别一上来就搞复杂的模型,容易把自己绕进去。

4.5 知识体系图

下面这张图展示了三种模型的关系和选择路径。我画了十几年架构图,这张算是比较经典的。

多核编程模型选择路径 业务需求分析 核数 ≤ 4? RTC模型 流程固定? Pipeline模型 Hybrid模型 特点:简单、易调试 局限:扩展性差 适用:小规模系统 特点:模块化、扩展好 局限:流水线平衡难 适用:大规模固定流程 特点:灵活、性能高 局限:设计复杂 适用:复杂混合业务

这张图我建议你保存下来。每次做架构设计时,先问自己两个问题:核数多不多?流程固定不固定?答案自然就有了。

避坑指南:我曾经在一个项目里,团队花了三个月设计了一个超级复杂的Hybrid模型。结果上线后发现,90%的流量走的是快速路径,根本用不到Pipeline部分。白白浪费了开发时间。所以我的建议是:先做简单版本跑起来,用数据说话,再决定要不要上复杂模型。

好了,三种模型都讲完了。记住一句话:没有最好的模型,只有最合适的模型。选型时多想想你的业务场景、团队能力、性能要求,别盲目追求高大上。

专注资料整理