llama.cpp/skills/cann_multi_stream_implement...

5.4 KiB
Raw Blame History

CANN Backend Multi-Stream Parallel Implementation

思考过程记录

1. 分析Vulkan后端的多流并行实现

通过分析PR #15489 和 #15850我了解到Vulkan后端的多流并行实现包含以下两个关键部分

1.1 PR #15489: 重写同步机制,允许节点之间的重叠执行

核心思想

  • 追踪需要同步的节点列表
  • 只有当新节点依赖于未完成的节点时才进行同步
  • 这允许一些重叠执行,从而提高性能

关键实现

  • 使用内存范围(地址)来判断依赖关系,而不是直接查看图结构
  • 每个预分配的临时缓冲区如dequantization或split_k都有一个bool标记指示它们是否被使用过并需要同步
  • 性能提升在RTX 5090上部分模型性能提升约5-8%

1.2 PR #15850: 图排序优化,允许更多的并行执行

核心思想

  • 添加backend procgraph_optimize)允许后端修改计算图
  • Vulkan实现会分析哪些节点相互依赖并贪婪地重排序它们
  • 将不相互依赖的节点分组在一起

关键实现

  • ggml_vk_graph_optimize函数实现图优化
  • 保留特定的fusion pattern不被重排序如RMS_NORM + MUL
  • 使用两遍扫描:第一遍抓取"real"节点第二遍抓取view节点
  • 最多检查接下来的20个节点是否可以提前执行

2. CANN后端当前状态分析

现有基础设施

  • 已有stream管理cann_ctx->stream()
  • 支持ACL Graph模式
  • 已有同步机制(aclrtSynchronizeStream
  • 后端接口中 graph_optimize 目前为NULL

需要添加的功能

  1. 实现 ggml_backend_cann_graph_optimize 函数
  2. 可能需要添加多流支持
  3. 添加环境变量控制开关

3. 设计方案

3.1 实现 graph_optimize 函数

参考Vulkan的实现我们需要

static void ggml_backend_cann_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * graph);

核心逻辑

  1. 判断是否禁用优化(环境变量控制)
  2. 定义辅助函数判断节点是否为"空"VIEW, RESHAPE等
  3. 定义辅助函数判断节点依赖关系
  4. 重排序算法:
    • 遍历所有未使用的节点
    • 找到可以与当前节点并行执行的节点(不相互依赖)
    • 保留fusion pattern
    • 更新节点顺序

3.2 环境变量

  • GGML_CANN_DISABLE_GRAPH_OPTIMIZE: 禁用图优化

4. 实现计划

  1. ggml-cann.cpp 中实现 ggml_backend_cann_graph_optimize
  2. ggml_backend_cann_interface 中注册该函数
  3. 编译验证
  4. 使用Qwen 0.5B模型验证功能正确性

5. 预期收益

根据Vulkan后端的测试结果图优化可以带来

  • 小模型1B参数约5-8%的性能提升
  • 中等模型8B参数约3-4%的性能提升
  • MoE模型约6-7%的性能提升

这些收益来自于减少同步次数,允许更多操作并行执行。

实现代码

修改文件

ggml/src/ggml-cann/ggml-cann.cpp

主要更改

  1. 添加头文件

    • <algorithm> - 用于 std::find
    • <set> - 用于 std::set
    • <vector> - 用于 std::vector
  2. 实现 ggml_backend_cann_graph_optimize 函数

    • 位于 ggml_backend_cann_event_wait 函数之后
    • 约250行代码
    • 参考Vulkan后端的实现
  3. 注册到backend interface

    • 修改 ggml_backend_cann_interface 结构体
    • graph_optimizeNULL 改为 ggml_backend_cann_graph_optimize

关键算法

// 核心优化算法伪代码
while (还有未处理的节点) {
    current_set = [下一个未处理的节点]
    
    // 保留fusion pattern
    if (match_pattern(ADD + RMS_NORM)) {
        keep_pattern_together()
        continue
    }
    
    // 第一遍:抓取可并行执行的"real"节点
    for (接下来的20个节点) {
        if (节点不依赖于未处理的节点) {
            if (支持fusion pattern) {
                add_to_current_set()
            }
        }
    }
    
    // 第二遍抓取view节点
    for (接下来的20个节点) {
        if (is_empty(节点) && 依赖已满足) {
            add_to_current_set()
        }
    }
    
    // 更新节点顺序
    new_order.append(current_set)
}

支持的Fusion Pattern

  • RMS_NORM + MUL
  • MUL_MAT + ADD
  • MUL_MAT_ID + ADD
  • ADD + ADD
  • ADD + RMS_NORMCANN特有
  • ROPE + VIEW + SET_ROWS

测试结果

测试环境

  • 模型Qwen 2.5 0.5B Instruct FP16
  • 设备4x Ascend 910B4
  • 测试命令:llama-cli -m qwen2.5:0.5b-instruct-fp16 -n 50 -ngl 99

测试输出

> Hello, how are you?

Hello! I'm Qwen, an AI developed by Alibaba Cloud. I'm here to answer any questions you may have and help with anything else you need help with. How can I assist you today?

[ Prompt: 1346.4 t/s | Generation: 142.8 t/s ]

结论

  • 编译通过
  • 模型加载成功
  • 推理输出正确
  • 正常退出

使用方法

启用图优化(默认)

./llama-cli -m model.gguf -ngl 99

禁用图优化

GGML_CANN_DISABLE_GRAPH_OPTIMIZE=1 ./llama-cli -m model.gguf -ngl 99

后续优化建议

  1. 添加性能测试使用llama-bench进行before/after性能对比
  2. 多流支持进一步实现真正的多流并行利用CANN的多stream能力
  3. 更多fusion pattern根据CANN的特性添加更多融合优化
  4. 环境变量调优:添加更细粒度的控制参数