# 方向3: 加速器协同调度 (CPU + NPU/GPU) ## 1. 问题陈述 现代嵌入式AI SoC都包含专用加速器(NPU/GPU/DSP),LLM推理需要CPU和加速器协同工作。核心问题: 1. **速度不匹配**: CPU和加速器的速度比通常是1:5~1:10,容易产生stall 2. **状态不可见**: NPU驱动通常封闭,无法精确感知NPU状态 3. **数据传输开销**: CPU↔NPU的数据传输是瓶颈 4. **同步开销**: barrier/semaphore的实时性分析复杂 ## 2. 加速器架构分析 ### 2.1 常见加速器类型 ``` ┌─────────────────────────────────────────────────────┐ │ Accelerator Types │ ├──────────────────┬───────────────────────────────────┤ │ NPU │ Neural Processing Unit │ │ │ • 专为矩阵乘设计 │ │ │ • 低精度(INT8/FP16) │ │ │ • 固定function, 可编程interface │ │ │ 例: 骁龙Hexagon, RK3588 NPU │ ├──────────────────┼───────────────────────────────────┤ │ GPU │ Graphics Processing Unit │ │ │ • 通用并行计算 │ │ │ • 高带宽, 高功耗 │ │ │ • 成熟软件栈(CUDA/OpenCL) │ │ │ 例: Adreno, Mali, PowerVR │ ├──────────────────┼───────────────────────────────────┤ │ DSP │ Digital Signal Processor │ │ │ • 向量/标量并行 │ │ │ • 低功耗, 适合小模型 │ │ │ 例: Hexagon DSP, Kryo │ ├──────────────────┼───────────────────────────────────┤ │ DPU │ Deep Learning Processing Unit │ │ │ • 固定功能, 低延迟 │ │ │ • 适合known architecture │ │ │ 例: Xilinx DPU, 平头哥玄铁 │ └──────────────────┴───────────────────────────────────┘ ``` ### 2.2 CPU-加速器通信路径 ``` CPU Accelerator ┌─────────────┐ ┌──────────────┐ │ │ DMA/Bus │ │ │ Task Queue │─────────────→│ Command Q │ │ │ │ │ │ Memory │←──DMA/Bus───│ Memory │ │ Pool │ │ Pool │ │ │ │ │ │ Event Q │←────────────│ Interrupt │ └─────────────┘ └──────────────┘ 通信机制: 1. Shared Memory: 零拷贝,通过DMA传递数据 2. Command Queue: CPU写入命令,加速器执行后完成 3. Interrupt: 加速器完成后的异步通知 4. Memory Barrier: 保证CPU和加速器看到的内存一致性 ``` ## 3. 协同调度模型 ### 3.1 典型LLM推理的CPU-加速器交互 ``` ┌─────────────────────────────────────────────────────────────┐ │ Phase: Prefill (Batch Inference) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ CPU: Accelerator: │ │ ┌──────────┐ ┌──────────────────┐ │ │ │Tokenizer │ DMA→ │ │ │ │ └──────────┘ │ Embedding │ │ │ ↓ └────────┬─────────┘ │ │ ┌──────────┐ ┌────────┴─────────┐ │ │ │Control │──Cmd──→ │ Attention L1..N │ │ │ │ (plan) │ └────────┬─────────┘ │ │ └──────────┘ ┌────────┴─────────┐ │ │ ↓ │ FFN L1..N │ │ │ ┌──────────┐ └────────┬─────────┘ │ │ │Monitor │←──IRQ──│ │ │ │ │ (wait) │ ┌────────────┐│ │ └──────────┘ ┌───────────────────┐│ Output ││ │ │ KV Cache Write │└─────┬──────┘│ │ └───────────────────┘ │ │ └─────────────────────────────────────────────────────────────┘ Timeline: CPU: | Tokenize | Plan | Wait | Monitor | Process Output | NPU: |-------- Embedding + Layers + Output --------| Time: 5ms 20ms (NPU compute) 5ms Total E2E: ~30ms (mostly dominated by NPU) ``` ### 3.2 解码阶段的流水线 ``` Decode Phase (逐token生成): Step i: Step i+1: ───────── ───────── CPU: Send Cmd NPU: Execute Layer 1..N Wait ←───────────────→ CPU: Send Cmd NPU: Execute Layer 1..N 问题: CPU SendCmd 和 NPU Execute 之间有gap 原因: NPU完成前CPU需等待 优化: Double Buffering ────────────────────────────── CPU: Send(i) Send(i+1) Send(i+2) NPU: Exec(i) Exec(i+1) Exec(i+2) 实现: - 两个buffer: buf_A, buf_B - CPU写buf_A时,NPU读buf_A - 完成后swap: CPU写buf_B,NPU读buf_B - 需要RTOS semaphore管理buffer swap ``` ## 4. 同步与通信机制 ### 4.1 同步原语 ```c // NPU协同同步原语 typedef struct { // 命令同步 SemaphoreHandle_t cmd_complete; // 命令完成信号 SemaphoreHandle_t data_ready; // 数据就绪信号 // 双缓冲管理 uint8_t *buffers[2]; // 双buffer uint8_t active_buf; // 当前active buffer SemaphoreHandle_t buf_swap; // buffer交换信号 // 事件通知 EventGroupHandle_t events; // 事件组 } npu_sync_t; // 同步流程 void npu_sync_wait(npu_sync_t *sync) { // 等待NPU完成当前命令 xSemaphoreTake(sync->cmd_complete, portMAX_DELAY); // 交换buffer xSemaphoreTake(sync->buf_swap, portMAX_DELAY); } void npu_sync_signal(npu_sync_t *sync) { // NPU完成中断回调 xSemaphoreGiveFromISR(sync->cmd_complete, NULL); xSemaphoreGiveFromISR(sync->buf_swap, NULL); } ``` ### 4.2 中断处理策略 ``` ┌─────────────────────────────────────────────┐ │ Interrupt Hierarchy │ ├─────────────────────────────────────────────┤ │ Level 0: Hard IRQ (NPU完成中断) │ │ 动作: 提升task优先级, 触发barrier │ │ 时间: <10μs │ ├─────────────────────────────────────────────┤ │ Level 1: SW IRQ (NPU驱动层) │ │ 动作: 释放semaphore, 唤醒task │ │ 时间: <50μs │ ├─────────────────────────────────────────────┤ │ Level 2: Soft IRQ (任务调度) │ │ 动作: 调度下一个task │ │ 时间: <100μs │ └─────────────────────────────────────────────┘ 关键设计: - NPU完成中断 → 直接唤醒attention task (不经过软中断) - 使用Interrupt-to-Semaphore模式, 避免context switch开销 - 中断处理函数最小化, 尽量defer到task层 ``` ## 5. 调度策略 ### 5.1 Pipeline并行调度 ``` Layer Pipeline (各层在加速器上流水执行): Layer: L1 L2 L3 L4 [Attn][FFN][Attn][FFN][Attn][FFN][Attn][FFN] CPU: [Plan] [Plan] [Plan] [Plan] [Collect] Time: ↓ ↓ ↓ ↓ Scheduling: 1. CPU先plan Layer 1的Attention 2. L1 Attn执行时, CPU plan L2 Attn 3. L1 Attn完成 → L1 FFN启动 4. CPU plan L3 Attn ... RTOS实现: - 每个Layer的Attn/FFn是一个task - CPU上跑"planner" task - 加速器上跑"compute" task - 通过barrier同步相邻Layer ``` ### 5.2 Cross-device Scheduling (多加速器) ``` Example: SoC with both NPU + GPU Request A: Large matrix → NPU (矩阵乘优化) Request B: Conv/Embed → GPU (并行度高) Request C: Small ops → DSP (低功耗) 调度问题: - 哪个加速器处理哪个请求? - 资源冲突时如何仲裁? - 数据在设备间传输的开销? 调度策略: 1. Capability-based: 根据加速器能力分配 2. Load-based: 根据当前负载分配 3. Hybrid: capability + load RTOS实现: typedef struct { enum accel_type { ACCEL_NPU, ACCEL_GPU, ACCEL_DSP } type; uint32_t utilization; // 当前利用率 uint32_t queue_depth; // 排队深度 uint64_t avg_latency_us; // 平均延迟 SemaphoreHandle_t lock; // 资源锁 QueueHandle_t cmd_queue; // 命令队列 } accel_resource_t; accel_resource_t* select_accelerate(task_t *task) { // 1. Filter by capability if (task->compute_type == MATRIX_MUL) return npu; if (task->compute_type == CONV) return gpu; // 2. Pick least loaded return min_utilization(all_accelerators); } ``` ### 5.3 Async Pipeline Scheduling ``` CPU side (RTOS task): ┌────────────────────────────────────────────┐ │ Task: NPU Dispatcher (priority: HIGH) │ │ │ │ while (running) { │ │ cmd = dequeue_command(); │ │ send_to_npu(cmd); │ │ wait_for_irq(); │ // 阻塞等待 │ if (complete) { │ │ process_result(); │ │ dispatch_next(); │ │ } │ │ } │ └────────────────────────────────────────────┘ NPU side (hardware): ┌────────────────────────────────────────────┐ │ Command Queue (FIFO): │ │ ├─ Cmd 1: Attention Layer 1 │ │ ├─ Cmd 2: FFN Layer 1 │ │ ├─ Cmd 3: Attention Layer 2 │ │ └─ ... │ │ │ │ Execution: Sequential (FIFO) or │ │ Out-of-order (with dependencies)│ └────────────────────────────────────────────┘ ``` ## 6. 数据传输优化 ### 6.1 DMA策略 ``` 传输类型 | 策略 | 优化手段 ---------------------|------------------------|------------------ CPU→NPU (weights) | 一次性批量DMA | PCIe burst CPU→NPU (input) | 流水线DMA | 预读 NPU→CPU (output) | 完成中断+DMA pull | 零拷贝 NPU→NPU (cross) | 共享内存 + cache sync | invalidate+clean DMA Descriptor设计: typedef struct { uint32_t src_addr; // 源地址 uint32_t dst_addr; // 目的地址 uint32_t size; // 传输大小 uint32_t flags; // 同步/异步/中断 uint32_t completion_irq; // 完成后是否触发中断 uint32_t next_desc; // 链式DMA描述符 } dma_desc_t; // 链式DMA: 多个描述符连成链, 一次性提交 // 减少RTOS调用次数, 提高DMA效率 void dma_chain_submit(dma_desc_t *head) { // Submit chain to DMA controller // No RTOS calls needed during transfer // Only interrupt on last descriptor } ``` ### 6.2 零拷贝技术 ``` Before: CPU alloc: malloc(input) // 分配 memcpy: copy(input) // 拷贝到临时buffer DMA: transfer(temp) // DMA从temp传输 Total: 2 copies + 1 DMA After (zero-copy): CPU alloc: mmap(shared_mem) // 共享内存 Direct: write(shared) // CPU直接写 DMA: transfer(shared) // DMA直接从shared传输 Total: 0 copies + 1 DMA RTOS实现: - 使用mmap或共享内存驱动 - CPU和NPU使用同一块物理内存 - 通过memory barrier保证一致性 - 使用RTOS semaphore管理访问权限 ``` ## 7. 各平台的加速器协同差异 ### 7.1 MCU级 ``` 典型: STM32H7 + Cortex-M7 (CPU) + DSP (协处理器) - 无NPU, 纯CPU/DSP矩阵运算 - 共享SRAM, 无DMA或简单DMA - 协同简单: CPU调用DSP函数, 等待完成 关键优化: - DSP的并行化调度 - 内存bank切换减少bank冲突 - 无共享内存, 需手动memcpy ``` ### 7.2 SoC级 ``` 典型: 骁龙8 Gen + Kryo CPU + Hexagon NPU - NPU驱动封闭, 使用QMI接口通信 - 共享内存通过RPMsg (Remote Processor Messaging) - 中断: ARM GIC → Hexagon 关键挑战: - NPU状态不完全可见 - QMI通信有固定开销(~100μs) - 需估算NPU延迟, 不能精确测量 RTOS策略: - 使用QMI异步API - 通过Event FD等待NPU完成 - 双Buffer管理QMI传输 ``` ### 7.3 Edge盒子 ``` 典型: Jetson Orin + ARM CPU + NVIDIA GPU - GPU驱动开放, CUDA API可用 - 成熟的stream/executor模型 - PCIe x16连接 关键优势: - 可精确控制GPU调度 - CUDA Stream可映射为RTOS task - NVLink多GPU调度成熟 RTOS集成: - CUDA Runtime作为RTOS task的一部分 - GPU completion → RTOS event - DMA between CPU↔GPU via PCIe ``` ### 7.4 Server级 ``` 典型: x86 + 多GPU + NVLink - 成熟的vLLM/TGI框架 - PCIe/NVLink互联 - NUMA拓扑 RTOS角色: - 管理GPU进程 - 处理NVLink中断 - 提供实时性保证给GPU inference 调度: - vLLM already does PagedAttention scheduling - RTOS主要保障中断响应 ``` ## 8. 关键设计决策 | 决策点 | 选项 | 推荐 | 理由 | |-------|------|-----|------| | 同步模式 | Sync / Async / Event | **Async + Event** | 最大化并行度 | | 缓冲策略 | Single / Double / Triple | **Triple** | 最大化流水线效率 | | 数据传输 | Copy / DMA / Shared | **DMA + Shared** | 零拷贝+低CPU占用 | | 加速决策 | Static / Dynamic | **Dynamic** | 根据负载自动选择 | | 中断模式 | Polling / Interrupt / Event | **Interrupt** | 实时性最好 | | 错误处理 | Retry / Skip / Report | **Retry + Report** | 保证正确性 | ## 9. 开放研究问题 1. **Black-box NPU Scheduling**: 加速状态不可知时的调度策略? 2. **Cross-accelerator Load Balancing**: 多加速器间的动态负载均衡? 3. **Accelerator Fault Recovery**: 加速器故障时的降级调度? 4. **Predictive Pipeline**: 预测NPU延迟, 优化pipeline stall? 5. **Heterogeneous Memory**: CPU/NPU共享内存的一致性管理? --- *最后更新: 2026-09-17*