17 KiB
17 KiB
方向3: 加速器协同调度 (CPU + NPU/GPU)
1. 问题陈述
现代嵌入式AI SoC都包含专用加速器(NPU/GPU/DSP),LLM推理需要CPU和加速器协同工作。核心问题:
- 速度不匹配: CPU和加速器的速度比通常是1:5~1:10,容易产生stall
- 状态不可见: NPU驱动通常封闭,无法精确感知NPU状态
- 数据传输开销: CPU↔NPU的数据传输是瓶颈
- 同步开销: 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 同步原语
// 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. 开放研究问题
- Black-box NPU Scheduling: 加速状态不可知时的调度策略?
- Cross-accelerator Load Balancing: 多加速器间的动态负载均衡?
- Accelerator Fault Recovery: 加速器故障时的降级调度?
- Predictive Pipeline: 预测NPU延迟, 优化pipeline stall?
- Heterogeneous Memory: CPU/NPU共享内存的一致性管理?
最后更新: 2026-09-17