犀牛鸟 2026 研究笔记 estelledc.github.io

Ch15: Paddle-Lite——万能充电器

Part 3: 六大框架架构精讲 前置章节:Ch14: TNN——ncnn 的架构翻新版 后续章节:Ch16: MACE——深入骨髓的硬件适配


一句话定位

Paddle-Lite 是六大引擎中后端数量最多的一个。它不像 ncnn 那样专注于 ARM CPU 极致优化,也不像 MNN 那样用 Geometry Compiler 做框架级创新,而是用一套精心设计的”硬件抽象层”——NNAdapter HAL——让同一个模型能跑在 ARM CPU、GPU(OpenCL)、华为 NPU(Kirin)、高通 DSP(Hexagon)、联发科 APU、瑞芯微 NPU、寒武纪 MLU 等 13+ 种异构后端上。它就像一个万能充电器——你不需要为每种手机准备一根不同的充电线,一个充电器配上不同的转接头就能给所有设备充电。

GitHub 7K star,百度开源,约 3000 个源文件——PaddlePaddle 生态的端侧推理组件。


类比:万能充电器的工作方式

你出国旅行,带了手机、平板、笔记本电脑。每个国家的插座标准不同——中国是扁脚、美国是圆脚、英国是三角脚、欧洲又不一样。最笨的方式是为每个国家带一套专用充电器。聪明的方式是带一个万能充电器:一个变压器模块(核心)加上一组可拆卸的转接头(适配器)。

万能充电器的设计要点:

  1. 核心变压器对所有转接头来说都是一样的(统一的内部表示)
  2. 每个转接头只需要实现”怎么插入当地插座”(硬件特定的适配逻辑)
  3. 添加一个新国家的支持,只需要做一个新转接头,不需要改变压器(开放扩展)
  4. 充电器会自动检测当前插座的电压并适配(运行时自动选择)

Paddle-Lite 的 NNAdapter HAL 就是这个万能充电器的架构:

类比边界:万能充电器的类比强调的是 Paddle-Lite 的”多后端适配”能力。但充电器是一个简单的单向流程(电源到设备),推理引擎有复杂的图优化、内存管理、异构调度等维度,这些超出了充电器类比的范围。


设计哲学精讲

哲学一:NNAdapter HAL——8 个函数指针统一异构硬件

NNAdapter 是 Paddle-Lite 最核心的设计创新。它定义了一个极简的 HAL(Hardware Abstraction Layer,硬件抽象层),让任何硬件后端只需要实现 8 个函数指针就能接入 Paddle-Lite。

// NNAdapter HAL 接口(简化)
typedef struct {
    // 设备管理
    int (*open_device)(const char* name, NNAdapterDevice** device);
    int (*close_device)(NNAdapterDevice* device);
    
    // 上下文管理
    int (*create_context)(NNAdapterDevice* device,
                           const char* properties,
                           NNAdapterContext** context);
    int (*destroy_context)(NNAdapterContext* context);
    
    // 模型编译
    int (*create_compilation)(NNAdapterModel* model,
                               NNAdapterContext* context,
                               NNAdapterCompilation** compilation);
    int (*destroy_compilation)(NNAdapterCompilation* compilation);
    
    // 推理执行
    int (*create_execution)(NNAdapterCompilation* compilation,
                             NNAdapterExecution** execution);
    int (*execute)(NNAdapterExecution* execution);
} NNAdapterHAL;

这 8 个函数覆盖了一个硬件后端的完整生命周期:打开/关闭设备、创建/销毁上下文、编译模型、执行推理。每个硬件厂商实现这 8 个函数,就完成了对 Paddle-Lite 的适配。

这种设计的灵感来自 Android 的 HAL 架构——Android 用 HAL 接口让不同厂商的硬件驱动可以无缝接入 Android 系统。Paddle-Lite 用同样的思路,让不同的 AI 加速器可以无缝接入 Paddle-Lite。

和 ORT 的 Execution Provider 对比:ORT 的 EP 接口有几十个方法(GetCapability、Compile、GetAllocator、OnRunStart 等等),实现一个 EP 是一个不小的工程。NNAdapter 的 8 个函数指针明显更轻量——代价是灵活性较低(EP 可以和 ORT 做更深度的集成,NNAdapter 的后端只能在”整图或子图”粒度上工作)。

NNAdapter 的工作流程:

1. Paddle-Lite 把模型图转换为 NNAdapter Model(标准中间表示)
2. NNAdapter Model 被传给硬件厂商的 HAL 实现
3. HAL 的 create_compilation 把 NNAdapter Model 编译为硬件特定的程序
4. HAL 的 execute 在硬件上执行推理
5. 如果某些 Op 硬件不支持,回退到 CPU 执行

目前通过 NNAdapter HAL 接入的硬件包括:华为 Kirin NPU(HiAI)、高通 Hexagon DSP、联发科 APU(NeuroPilot)、瑞芯微 NPU(RKNN)、晶晨 NPU、寒武纪 MLU、比特大陆 Sophon 等。这个列表还在不断增长——每增加一个新硬件,只需要实现 8 个函数。

哲学二:独立 IR——从 PaddlePaddle 模型到统一中间表示

和 ncnn/MNN/TNN 直接消费自己的模型格式不同,Paddle-Lite 有一个独立的中间表示(IR)。它的作用是在 PaddlePaddle 模型格式和各种后端之间建立一个缓冲层。

PaddlePaddle 模型 -> [转换] -> Paddle-Lite IR -> [优化] -> 优化后 IR -> [执行]
                                                  ^
                                            50+ 优化 pass

Paddle-Lite IR 由三个核心概念组成:

// IR 的核心概念
class OpLite {              // 算子节点
    std::string type_;      // 算子类型(如 "conv2d")
    OpInfo* op_info_;       // 参数信息
};

class Scope {               // 变量作用域
    std::map<std::string, Variable*> vars_;
    // 所有 Tensor 都通过 Scope 管理
};

class SSAGraph {             // 计算图(静态单赋值形式)
    std::list<mir::Node*> nodes_;  // 节点列表
    // mir = Model IR
};

独立 IR 的价值在于:所有的图优化 pass 都作用在 IR 上,而不是作用在 PaddlePaddle 的原始模型表示上。这让优化逻辑和模型格式解耦——如果未来 Paddle-Lite 要支持 ONNX 模型,只需要加一个”ONNX 到 Paddle-Lite IR”的前端转换器,所有已有的优化 pass 都可以直接复用。

哲学三:TensorLite——三维坐标描述 Tensor

Paddle-Lite 的 Tensor(TensorLite)有一个独特的设计:用三维坐标 (TargetType, PrecisionType, DataLayoutType) 描述 Tensor 的完整属性。

// TensorLite(简化)
class TensorLite {
    TargetType target_;        // 数据在哪种设备上
    PrecisionType precision_;  // 数据的精度
    DataLayoutType layout_;    // 数据的内存布局
    
    DDim dims_;                // 形状
    void* raw_data_;           // 数据指针
    size_t memory_size_;       // 内存大小
    
    bool persistable_;         // 是否是模型权重(不会被释放)
};

// 三维坐标的取值
enum class TargetType {
    kHost,          // CPU 通用
    kARM,           // ARM CPU(有 NEON 优化)
    kOpenCL,        // GPU (OpenCL)
    kMetal,         // GPU (Metal, iOS)
    kNNAdapter,     // NPU/DSP (通过 NNAdapter)
    kFPGA,          // FPGA
    // ...
};

enum class PrecisionType {
    kFloat,         // fp32
    kFP16,          // fp16
    kInt8,          // int8
    kInt32,         // int32
    kBool,          // bool
    // ...
};

enum class DataLayoutType {
    kNCHW,          // 标准布局
    kNHWC,          // TensorFlow 风格
    kImageDefault,  // OpenCL image2d 默认
    kImageFolder,   // OpenCL image2d 折叠
    // ...
};

这个三维坐标系统的精妙之处在于:它把 Tensor 的属性从”隐式约定”变成了”显式声明”。在 ncnn 中,你看到一个 Mat,不知道它是 CPU 上的还是 GPU 上的(CPU 用 Mat,GPU 用 VkMat,但这是类型的区别,不是属性的区别)。在 Paddle-Lite 中,一个 TensorLite 通过 (kOpenCL, kFP16, kImageDefault) 精确描述”这是一个在 OpenCL 设备上、fp16 精度、image2d 默认布局的 Tensor”。

这个三维坐标在后面要讲的 static_kernel_pick_pass 和类型转换自动插入中扮演关键角色。

哲学四:MIR 50+ Pass——工业级的图优化管线

Paddle-Lite 拥有六大框架中最多的图优化 pass——超过 50 个。这些 pass 组成了一条完整的图优化管线,从模型加载到推理执行之间要经过大量的自动化处理。

优化管线(选取关键 pass):

前端处理:
  lite_quant_dequant_fuse_pass      # 量化/反量化融合
  lite_conv_bn_fuse_pass            # Conv+BN 融合
  lite_conv_activation_fuse_pass    # Conv+Activation 融合
  lite_fc_fuse_pass                 # FC 层融合
  
中端优化:
  static_kernel_pick_pass           # 内核选择(核心 pass)
  type_precision_cast_pass          # 精度转换插入
  type_layout_cast_pass             # 布局转换插入
  type_target_cast_pass             # 设备转换插入
  
后端处理:
  memory_optimize_pass              # 内存优化
  nnadapter_subgraph_partition_pass # NNAdapter 子图分区

这么多 pass 存在的原因是:Paddle-Lite 要支持 13+ 种后端,每种后端对数据精度、布局、算子融合的需求都不一样。比如 ARM CPU 偏好 NCHW + fp32、OpenCL GPU 偏好 ImageDefault + fp16、NPU 偏好 NHWC + int8。这些差异不能在一个 pass 里处理,需要分解为多个正交的 pass,每个 pass 只关注一个维度的适配。

哲学五:static_kernel_pick_pass——运行时内核选择

static_kernel_pick_pass 是 Paddle-Lite 最关键的优化 pass。它的作用是:为模型中的每个 Op 选择最优的内核实现

一个 Op(比如 Conv2D)在 Paddle-Lite 中可能有多个内核实现:

Conv2D 的可用内核:
  1. ARM + fp32 + NCHW   -> arm_conv_compute (NEON 优化)
  2. ARM + int8 + NCHW   -> arm_conv_int8_compute (int8 量化)
  3. ARM + fp16 + NCHW   -> arm_conv_fp16_compute (fp16)
  4. OpenCL + fp16 + ImageDefault -> opencl_conv_image_compute
  5. OpenCL + fp32 + Buffer -> opencl_conv_buffer_compute
  6. NNAdapter + any + any -> nnadapter_subgraph_compute

static_kernel_pick_pass 根据用户指定的目标后端和 Op 的输入/输出 Tensor 属性,选择最匹配的内核:

// static_kernel_pick_pass 的核心逻辑(概念简化)
void StaticKernelPickPass::Apply(SSAGraph* graph) {
    for (auto& node : graph->mutable_nodes()) {
        if (!node.IsStmt()) continue;
        
        auto& stmt = node.AsStmt();
        auto op_type = stmt.op_type();
        
        // 获取所有可用内核
        auto kernels = KernelRegistry::Global().Create(
            op_type, target_, precision_, layout_);
        
        // 按优先级排序
        //   1. Target 匹配(设备类型): 权重最高
        //   2. Precision 匹配(精度): 权重中等
        //   3. Layout 匹配(布局): 权重较低
        std::sort(kernels.begin(), kernels.end(),
                  [](const auto& a, const auto& b) {
            return Score(a) > Score(b);
        });
        
        // 选择得分最高的内核
        stmt.SetKernels(std::move(kernels));
    }
}

选择内核后,可能出现”不匹配”的情况:比如前一层的输出是 (kARM, kFloat, kNCHW),但后一层选中的内核需要 (kOpenCL, kFP16, kImageDefault) 的输入。这时候需要自动插入类型转换节点。

哲学六:三类 Cast 自动插入——无缝的异构计算

Paddle-Lite 定义了三类自动插入的转换节点,分别处理 TensorLite 三维坐标中的每一个维度:

1. type_target_cast_pass: 设备转换
   (kARM, fp32, NCHW) -> [io_copy] -> (kOpenCL, fp32, NCHW)
   实际操作: CPU 内存到 GPU 内存(memcpy host-to-device)

2. type_precision_cast_pass: 精度转换
   (kOpenCL, fp32, NCHW) -> [precision_cast] -> (kOpenCL, fp16, NCHW)
   实际操作: fp32 到 fp16 数值转换

3. type_layout_cast_pass: 布局转换
   (kOpenCL, fp16, NCHW) -> [layout_cast] -> (kOpenCL, fp16, ImageDefault)
   实际操作: Buffer 到 Image2D 格式转换

这三类 cast 的自动插入机制让用户完全不需要关心异构计算的数据搬运问题。用户只需要说”我要用 OpenCL 后端”,Paddle-Lite 自动在需要的地方插入 CPU-GPU 数据传输、精度转换和布局转换。

自动插入示例:

用户模型(纯逻辑层面):
  Input -> Conv -> ReLU -> Resize -> Conv -> Output

用户指定 OpenCL 后端后,Paddle-Lite 自动插入 cast:
  Input(CPU)
  -> [io_copy: CPU->GPU]
  -> [layout_cast: Buffer->Image2D]
  -> Conv(OpenCL, fp16, Image2D)
  -> ReLU(OpenCL, fp16, Image2D)
  -> [layout_cast: Image2D->Buffer]     # Resize 不支持 Image2D
  -> [io_copy: GPU->CPU]
  -> Resize(CPU, fp32, NCHW)            # 回退到 CPU 执行
  -> [io_copy: CPU->GPU]
  -> [layout_cast: Buffer->Image2D]
  -> Conv(OpenCL, fp16, Image2D)
  -> [layout_cast: Image2D->Buffer]
  -> [io_copy: GPU->CPU]
  -> Output(CPU)

这种自动化的代价是:如果回退到 CPU 的 Op 很多,频繁的 CPU-GPU 数据传输会严重拖慢推理速度。在实际使用中,需要确保模型中的大部分 Op 都有目标后端的内核实现,否则性能可能不如纯 CPU 推理。


核心数据结构:TensorLite

TensorLite 的完整属性:

class TensorLite {
    // 三维坐标
    TargetType target_;         // kHost / kARM / kOpenCL / kNNAdapter / ...
    PrecisionType precision_;   // kFloat / kFP16 / kInt8 / ...
    DataLayoutType layout_;     // kNCHW / kNHWC / kImageDefault / ...
    
    // 形状
    DDim dims_;                 // {1, 3, 224, 224}
    LoD lod_;                   // Level of Detail(PaddlePaddle 特有的序列信息)
    
    // 内存
    Buffer buffer_;             // 底层内存缓冲
    void* raw_data_;            // 数据指针
    size_t memory_size_;        // 已分配的内存大小
    size_t offset_;             // 在 buffer 中的偏移
    
    // 标记
    bool persistable_;          // 是否是模型权重
};

和其他框架的对比:

特性 ncnn Mat MNN Tensor TNN Blob ORT Tensor PL TensorLite
设备信息 内含 BlobDesc MemoryInfo TargetType
精度信息 elemsize halide_type DataType DataTypeImpl PrecisionType
布局信息 elempack DimensionType DataFormat DataLayoutType
序列信息 LoD

TensorLite 是六大框架中信息最完整的 Tensor 类型——它用三个独立的枚举显式表达了设备、精度、布局三个维度的信息。这种显式表达让自动化的类型转换插入成为可能(因为系统可以精确地知道两个 Tensor 在哪个维度上不匹配,然后插入对应的 cast)。

LoD(Level of Detail)是 PaddlePaddle 特有的概念,用于表示变长序列数据。这在 NLP 任务中有用(不同句子有不同长度),但在其他框架中没有对应的概念。


推理流程

Paddle-Lite 的推理流程比其他框架多了一个”离线优化”阶段。

阶段零:模型优化(离线)

# 使用 opt 工具对模型做离线优化
paddle_lite_opt --model_file=model.pdmodel \
                --param_file=model.pdiparams \
                --optimize_out=optimized_model \
                --valid_targets=arm

opt 工具执行所有 50+ 个优化 pass(包括 static_kernel_pick_pass 和三类 cast 插入),输出优化后的 .nb 文件(Naive Buffer 格式)。这个离线优化步骤是 Paddle-Lite 的特色——大量的优化工作在部署前完成,运行时的开销更小。

ncnn 也有类似的离线工具(ncnnoptimize),但它主要做算子融合,不涉及内核选择和类型转换插入。Paddle-Lite 的 opt 工具做的事情多得多。

阶段一:模型加载

MobileConfig config;
config.set_model_from_file("optimized_model.nb");
config.set_threads(4);

std::shared_ptr<PaddlePredictor> predictor =
    CreatePaddlePredictor<MobileConfig>(config);

加载优化后的 .nb 文件比加载原始 PaddlePaddle 模型快得多——因为所有的图优化已经做过了,加载时只需要反序列化和创建内核实例。

阶段二:推理执行

// 获取输入 Tensor
auto input = predictor->GetInput(0);
input->Resize({1, 3, 224, 224});
auto* input_data = input->mutable_data<float>();
memcpy(input_data, data, sizeof(float) * 3 * 224 * 224);

// 执行推理
predictor->Run();

// 获取输出
auto output = predictor->GetOutput(0);
const float* output_data = output->data<float>();

关键差异:Paddle-Lite 与其他框架

vs ncnn/MNN/TNN:后端数量

Paddle-Lite 通过 NNAdapter HAL 支持 13+ 种后端,远超 ncnn(CPU+Vulkan)、MNN(CPU+OpenCL+Metal+Vulkan+CUDA)和 TNN(CPU+OpenCL+Metal)。如果你的部署目标包含特殊硬件(华为 NPU、瑞芯微 NPU、FPGA),Paddle-Lite 可能是唯一的选择。

vs ORT:HAL vs EP

ORT 的 EP 接口更复杂但更灵活(EP 可以参与图优化、内核选择等多个阶段)。Paddle-Lite 的 NNAdapter HAL 更简单(8 个函数指针),适合硬件厂商快速接入,但灵活性不如 EP。

vs MNN:pass 驱动 vs 编译器驱动

MNN 用 Geometry Compiler 在运行时自动处理非计算 Op 的合并;Paddle-Lite 用 50+ 个显式的 pass 在编译时(离线优化)处理图优化。MNN 的方式更自动化但有运行时开销,Paddle-Lite 的方式更可控但需要离线优化步骤。


适用场景与局限

最适合 Paddle-Lite 的场景

部署到异构硬件的场景。如果你的产品需要在华为手机(Kirin NPU)、高通手机(Hexagon DSP)、瑞芯微开发板(RKNN NPU)等多种硬件上部署同一个模型,Paddle-Lite 通过 NNAdapter HAL 提供了统一的部署方案。

PaddlePaddle 生态的用户。如果你用 PaddlePaddle 训练模型,Paddle-Lite 提供了最无缝的部署体验——模型格式直接兼容,不需要先转成 ONNX 再转成其他格式。

NPU 加速场景。在六大框架中,Paddle-Lite 的 NPU 后端覆盖面最广。如果你需要利用手机 NPU 的算力,Paddle-Lite 比 ncnn(不支持 NPU)和 MNN(NPU 支持有限)是更好的选择。

Paddle-Lite 的局限

纯 CPU 性能不如 ncnn。Paddle-Lite 的 ARM CPU 内核虽然也有 NEON 优化,但手写汇编的精细程度不如 ncnn。在纯 CPU 推理的基准测试中,ncnn 通常更快。

生态绑定。Paddle-Lite 和 PaddlePaddle 生态绑定较深。虽然也支持通过 ONNX 转入其他框架的模型,但原生体验还是 PaddlePaddle 模型最好。如果你用 PyTorch,需要先转 ONNX 再转 Paddle,多了一步。

离线优化步骤。需要用 opt 工具做离线优化才能获得最佳性能。其他框架(ncnn、MNN)可以直接加载模型运行,Paddle-Lite 多了一个部署环节。

代码复杂度中等偏高。约 3000 个文件、50+ 个 pass、三维类型系统——Paddle-Lite 的代码复杂度介于 MNN(2000 文件)和 ORT(8000 文件)之间。对初学者来说,理解内核选择和类型转换的交互逻辑有一定门槛。

不支持端侧 LLM 和训练。和 ncnn、TNN 一样,Paddle-Lite 是纯推理引擎,没有端侧 LLM 优化和端侧训练能力。MNN 在这方面领先。


代码结构

Paddle-Lite 约 3000 个文件,结构如下:

Paddle-Lite/lite/
├── api/                               # 用户 API
│   ├── paddle_api.h                   # C++ API
│   └── python/                        # Python 绑定
│
├── core/                              # 核心抽象
│   ├── tensor.h / .cc                 # TensorLite
│   ├── kernel.h / .cc                 # KernelBase
│   ├── op_lite.h / .cc                # OpLite
│   ├── scope.h / .cc                  # 变量作用域
│   ├── program.h / .cc                # 程序表示
│   ├── context.h                      # 上下文(设备相关)
│   └── types.h                        # TargetType/PrecisionType/DataLayoutType
│
├── model_parser/                      # 模型解析
│   ├── pb/                            # PaddlePaddle protobuf 格式
│   ├── flatbuffers/                   # FlatBuffers 格式
│   └── naive_buffer/                  # NaiveBuffer 格式(.nb)
│
├── kernels/                           # 内核实现
│   ├── host/                          # 通用 CPU 内核
│   ├── arm/                           # ARM NEON 内核
│   │   ├── conv_compute.h / .cc
│   │   ├── conv_depthwise.h / .cc
│   │   └── ...
│   ├── opencl/                        # OpenCL 内核
│   │   ├── image2d/                   # image2d 路径
│   │   └── buffer/                    # buffer 路径
│   ├── metal/                         # Metal 内核 (iOS)
│   └── x86/                           # x86 内核
│
├── backends/                          # NNAdapter 后端
│   └── nnadapter/
│       ├── nnadapter/                 # NNAdapter 核心
│       │   ├── include/
│       │   │   └── nnadapter.h        # HAL 接口定义
│       │   └── src/
│       │       ├── driver/            # 各厂商 HAL 实现
│       │       │   ├── huawei_kirin_npu/
│       │       │   ├── qualcomm_qnn/
│       │       │   ├── mediatek_apu/
│       │       │   ├── rockchip_npu/
│       │       │   ├── cambricon_mlu/
│       │       │   └── ...
│       │       └── runtime/           # NNAdapter 运行时
│       └── ...
│
├── operators/                         # Op 定义
│   ├── conv_op.h / .cc
│   ├── relu_op.h / .cc
│   └── ...
│
└── core/optimizer/mir/                # MIR 图优化
    ├── pass.h                         # Pass 基类
    ├── pass_manager.h                 # Pass 管理器
    ├── fusion/                        # 融合 pass
    │   ├── conv_bn_fuse_pass.h / .cc
    │   └── ...
    ├── static_kernel_pick_pass.h / .cc
    ├── type_precision_cast_pass.h / .cc
    ├── type_layout_cast_pass.h / .cc
    ├── type_target_cast_pass.h / .cc
    └── memory_optimize_pass.h / .cc

关键阅读路径:

  1. 入门core/types.h(三维坐标定义)-> core/tensor.h(TensorLite)-> core/kernel.h(KernelBase)
  2. 理解 NNAdapterbackends/nnadapter/nnadapter/include/nnadapter.h(HAL 接口)-> 选一个 driver 实现阅读
  3. 理解内核选择core/optimizer/mir/static_kernel_pick_pass.cc
  4. 理解类型转换core/optimizer/mir/type_target_cast_pass.cc(设备转换)-> type_precision_cast_pass.cc(精度转换)-> type_layout_cast_pass.cc(布局转换)
  5. 理解 ARM 内核kernels/arm/conv_compute.cc

小结

Paddle-Lite 的设计哲学可以用一个词概括:适配性。它不追求在某一个后端上做到极致(那是 ncnn 在 ARM CPU 上做的事),而是追求让同一个模型能在尽可能多的硬件上跑。NNAdapter HAL 用 8 个函数指针定义了硬件接入的最小接口,TensorLite 的三维坐标精确描述了数据的设备/精度/布局属性,static_kernel_pick_pass 自动选择最优内核,三类 cast pass 自动插入数据转换节点。

它的核心创新在于把异构计算的复杂性封装在框架内部:用户不需要关心”数据从 CPU 搬到 GPU 需要什么操作”“fp32 到 fp16 的转换在哪里做”“NCHW 到 Image2D 的布局变换怎么处理”——这些全部由 Paddle-Lite 的 pass 管线自动完成。用户只需要指定目标后端,框架自动做完所有事情。

对于犀牛鸟竞赛的参赛者来说,Paddle-Lite 的 NNAdapter HAL 设计和三维坐标 TensorLite 值得学习——如果你需要为推理引擎加一个新的硬件后端,NNAdapter 的极简 HAL 思路比 ORT 的重量级 EP 接口更适合快速原型开发。而三类 cast pass 的自动插入机制,则是理解异构推理引擎如何管理跨设备数据流的绝佳案例。

下一章,也是 Part 3 的最后一章,我们将看到 MACE 如何用”深入骨髓的硬件适配”——OpenCL image2d 精细映射、Hexagon DSP 整图下推、功耗管理——在特定硬件上榨取最后一滴性能。


导读目录:README.md 上一章:Ch14: TNN——ncnn 的架构翻新版 下一章:Ch16: MACE——深入骨髓的硬件适配