CANN图引擎GE:深度解析AI模型编译与执行的核心架构

一、项目概述

CANN组织链接: https://atomgit.com/cann
ge仓库链接: https://atomgit.com/cann/ge

GE(Graph Engine)是 CANN 面向 NPU 的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。该项目在开源社区拥有超过 370 个 Star,是 CANN 生态中的核心组件。

1.1 核心定位

GE 作为连接 AI 框架与底层硬件的桥梁,负责将高级计算图转换为可在 NPU 上高效执行的低级表示。它提供对 PyTorch、TensorFlow 前端的友好接入能力,并同时支持 ONNX、PB 等主流模型格式的解析与编译。

1.2 技术特点

  • 图优化: 多种图优化技术,包括算子融合、常量折叠、死代码消除等
  • 多流并行: 支持多流并行执行,充分利用硬件资源
  • 内存优化: 内存复用、显存优化,降低内存占用
  • 模型下沉: 将部分计算下沉到设备端,减少主机-设备通信
  • 多框架支持: 支持 PyTorch、TensorFlow、ONNX 等主流框架

二、GE 架构设计

2.1 整体架构

┌─────────────────────────────────────────────────────────┐
│                    AI 框架层                              │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐   │
│  │ PyTorch │  │TensorFlow│  │ ONNX   │  │   ...   │   │
│  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘   │
├─────────┼──────────┼──────────┼──────────┼────────────┤
│         │          │          │          │             │
│  ┌─────▼──────────▼──────────▼──────────▼─────┐       │
│  │            GE 前端接口层                     │       │
│  │  ┌────────┐ ┌────────┐ ┌────────┐         │       │
│  │  │ Parser │ │ Loader │ │ Builder│         │       │
│  │  └────────┘ └────────┘ └────────┘         │       │
│  ├─────────────────────────────────────────────┤       │
│  │            GE 核心优化层                     │       │
│  │  ┌────────┐ ┌────────┐ ┌────────┐         │       │
│  │  │Pass    │ │ Fusion │ │ Memory │         │       │
│  │  │Manager │ │ Engine │ │ Opt    │         │       │
│  │  └────────┘ └────────┘ └────────┘         │       │
│  ├─────────────────────────────────────────────┤       │
│  │            GE 后端执行层                     │       │
│  │  ┌────────┐ ┌────────┐ ┌────────┐         │       │
│  │  │Stream  │ │ Task   │ │Event   │         │       │
│  │  │Manager │ │ Scheduler│ Mgr   │         │       │
│  │  └────────┘ └────────┘ └────────┘         │       │
│  └─────────────────────────────────────────────┘       │
├─────────────────────────────────────────────────────────┤
│                    运行时层 (Runtime)                    │
├─────────────────────────────────────────────────────────┤
│                    NPU 硬件层                            │
└─────────────────────────────────────────────────────────┘

2.2 计算图表示

/**
 * GE 计算图基础数据结构
 */

// 节点类型枚举
enum class NodeType {
    DATA,          // 数据节点
    OP,            // 算子节点
    NET_OUTPUT,    // 网络输出节点
    VALUE_PASS,    // 值传递节点
    CONTROL_FLOW   // 控制流节点
};

// 算子节点定义
class OpNode {
public:
    std::string name_;           // 节点名称
    std::string type_;           // 算子类型
    std::vector<Tensor> inputs_; // 输入张量
    std::vector<Tensor> outputs_; // 输出张量
    AttrMap attrs_;              // 算子属性

    // 优化标记
    bool is_fused_ = false;
    bool is_constant_folded_ = false;

    // 执行信息
    int stream_id_ = 0;          // 所属流 ID
    int priority_ = 0;           // 执行优先级
};

// 计算图定义
class ComputeGraph {
public:
    std::string name_;
    std::vector<std::shared_ptr<OpNode>> nodes_;
    std::vector<std::shared_ptr<Tensor>> tensors_;

    // 图属性
    bool is_dynamic_ = false;    // 是否动态图
    int device_id_ = 0;          // 设备 ID

    // 添加节点
    void AddNode(std::shared_ptr<OpNode> node) {
        nodes_.push_back(node);
    }

    // 添加张量
    void AddTensor(std::shared_ptr<Tensor> tensor) {
        tensors_.push_back(tensor);
    }

    // 拓扑排序
    std::vector<std::shared_ptr<OpNode>> TopologicalSort();
};

三、图优化技术

3.1 算子融合

/**
 * 算子融合引擎
 * 将多个算子融合为一个,减少内存访问和 kernel 启动开销
 */
class FusionEngine {
public:
    /**
     * 执行算子融合
     */
    void RunFusion(ComputeGraph* graph) {
        // 1. 识别可融合的模式
        auto patterns = FindFusionPatterns(graph);

        // 2. 对每个模式执行融合
        for (const auto& pattern : patterns) {
            ApplyFusion(graph, pattern);
        }

        // 3. 重构图
        RebuildGraph(graph);
    }

private:
    /**
     * 融合模式定义
     */
    struct FusionPattern {
        std::string name;
        std::vector<std::string> op_types;
        std::function<bool(const std::vector<OpNode*>&)> validator;
        std::string fused_op_type;
    };

    /**
     * 查找可融合的模式
     */
    std::vector<FusionMatch> FindFusionPatterns(ComputeGraph* graph) {
        std::vector<FusionMatch> matches;

        // 定义融合模式
        std::vector<FusionPattern> patterns = {
            // Conv2D + BiasAdd + ReLU
            {
                "ConvBNRelu",
                {"Conv2D", "BatchNorm", "Relu"},
                ValidateConvBNRelu,
                "FusedConvBNRelu"
            },
            // MatMul + BiasAdd + Gelu
            {
                "MatMulBiasGelu",
                {"MatMul", "BiasAdd", "Gelu"},
                ValidateMatMulBiasGelu,
                "FusedMatMulBiasGelu"
            },
            // 多个连续的 Element-wise 操作
            {
                "ElementWiseChain",
                {"Add", "Mul", "Relu"},
                ValidateElementWiseChain,
                "FusedElementWise"
            }
        };

        // 遍历图,匹配模式
        for (const auto& pattern : patterns) {
            auto pattern_matches = MatchPattern(graph, pattern);
            matches.insert(matches.end(),
                         pattern_matches.begin(),
                         pattern_matches.end());
        }

        return matches;
    }

    /**
     * 验证 Conv2D + BN + ReLU 模式
     */
    static bool ValidateConvBNRelu(const std::vector<OpNode*>& nodes) {
        if (nodes.size() != 3) return false;

        auto* conv = nodes[0];
        auto* bn = nodes[1];
        auto* relu = nodes[2];

        // 检查连接关系
        if (conv->outputs_[0] != bn->inputs_[0]) return false;
        if (bn->outputs_[0] != relu->inputs_[0]) return false;

        // 检查没有其他节点依赖中间结果
        if (conv->outputs_[0]->consumers.size() > 1) return false;
        if (bn->outputs_[0]->consumers.size() > 1) return false;

        return true;
    }

    /**
     * 应用融合
     */
    void ApplyFusion(ComputeGraph* graph,
                     const FusionMatch& match) {
        // 创建融合算子
        auto fused_node = CreateFusedNode(match);

        // 收集所有输入
        std::vector<Tensor*> inputs;
        for (auto* node : match.nodes) {
            inputs.insert(inputs.end(),
                        node->inputs_.begin(),
                        node->inputs_.end());
        }

        // 设置融合算子输入
        fused_node->inputs_ = inputs;

        // 收集所有输出
        std::vector<Tensor*> outputs;
        for (auto* node : match.nodes) {
            outputs.insert(outputs.end(),
                         node->outputs_.begin(),
                         node->outputs_.end());
        }

        // 设置融合算子输出
        fused_node->outputs_ = outputs;

        // 替换原图中的节点
        ReplaceNodes(graph, match.nodes, fused_node);
    }

    /**
     * 创建融合算子节点
     */
    std::shared_ptr<OpNode> CreateFusedNode(
            const FusionMatch& match) {
        auto fused_node = std::make_shared<OpNode>();

        // 设置算子类型
        fused_node->type_ = match.pattern.fused_op_type;
        fused_node->name_ = GenerateFusedNodeName(match);

        // 合并属性
        for (auto* node : match.nodes) {
            for (const auto& attr : node->attrs_) {
                fused_node->attrs_[attr.first] = attr.second;
            }
        }

        // 标记为已融合
        fused_node->is_fused_ = true;

        return fused_node;
    }
};

3.2 常量折叠

/**
 * 常量折叠 Pass
 * 在编译时计算常量表达式的值,减少运行时开销
 */
class ConstantFoldingPass {
public:
    void Run(ComputeGraph* graph) {
        bool changed = true;
        int iteration = 0;

        // 迭代执行,直到不再有变化
        while (changed && iteration < max_iterations_) {
            changed = false;
            iteration++;

            for (auto* node : graph->nodes_) {
                if (TryFoldNode(node)) {
                    changed = true;
                }
            }
        }

        // 清理死代码
        RemoveDeadNodes(graph);
    }

private:
    int max_iterations_ = 10;

    /**
     * 尝试折叠节点
     */
    bool TryFoldNode(OpNode* node) {
        // 检查所有输入是否为常量
        bool all_inputs_constant = true;
        for (auto* input : node->inputs_) {
            if (!input->is_constant) {
                all_inputs_constant = false;
                break;
            }
        }

        if (!all_inputs_constant) {
            return false;
        }

        // 根据算子类型执行折叠
        if (node->type_ == "Add") {
            return FoldBinaryOp<AddOp>(node);
        } else if (node->type_ == "Mul") {
            return FoldBinaryOp<MulOp>(node);
        } else if (node->type_ == "Relu") {
            return FoldUnaryOp<ReluOp>(node);
        }
        // ... 其他算子

        return false;
    }

    /**
     * 折叠二元操作
     */
    template<typename OpType>
    bool FoldBinaryOp(OpNode* node) {
        auto* left = node->inputs_[0];
        auto* right = node->inputs_[1];
        auto* output = node->outputs_[0];

        // 获取常量值
        const auto& left_data = left->constant_data;
        const auto& right_data = right->constant_data;

        // 分配输出内存
        output->constant_data.resize(output->NumElements());

        // 执行计算
        OpType op;
        for (size_t i = 0; i < output->NumElements(); ++i) {
            output->constant_data[i] = op.Compute(
                left_data[i], right_data[i]
            );
        }

        // 标记为常量
        output->is_constant = true;

        return true;
    }

    /**
     * 移除死代码(不再使用的节点)
     */
    void RemoveDeadNodes(ComputeGraph* graph) {
        std::set<OpNode*> alive_nodes;
        std::queue<OpNode*> queue;

        // 从输出节点开始反向遍历
        for (auto* node : graph->nodes_) {
            if (node->type_ == "NetOutput") {
                alive_nodes.insert(node);
                queue.push(node);
            }
        }

        // 反向遍历
        while (!queue.empty()) {
            auto* node = queue.front();
            queue.pop();

            for (auto* input : node->inputs_) {
                if (input->producer) {
                    auto* producer = input->producer;
                    if (alive_nodes.find(producer) == alive_nodes.end()) {
                        alive_nodes.insert(producer);
                        queue.push(producer);
                    }
                }
            }
        }

        // 移除死节点
        auto& nodes = graph->nodes_;
        nodes.erase(
            std::remove_if(nodes.begin(), nodes.end(),
                [&alive_nodes](const auto& node) {
                    return alive_nodes.find(node.get()) == alive_nodes.end();
                }),
            nodes.end()
        );
    }
};

3.3 内存优化

/**
 * 内存优化引擎
 * 实现内存复用和显存优化
 */
class MemoryOptimizer {
public:
    /**
     * 执行内存优化
     */
    void Optimize(ComputeGraph* graph) {
        // 1. 计算每个张量的生命周期
        auto lifetimes = ComputeTensorLifetimes(graph);

        // 2. 构建内存复用图
        auto reuse_plan = BuildReusePlan(lifetimes);

        // 3. 分配内存
        AllocateMemory(graph, reuse_plan);

        // 4. 优化内存布局
        OptimizeMemoryLayout(graph);
    }

private:
    /**
     * 张量生命周期信息
     */
    struct TensorLifetime {
        Tensor* tensor;
        int birth_step;   // 诞生步骤
        int death_step;   // 死亡步骤
        size_t size;      // 内存大小
    };

    /**
     * 计算张量生命周期
     */
    std::vector<TensorLifetime> ComputeTensorLifetimes(
            ComputeGraph* graph) {
        // 拓扑排序
        auto sorted_nodes = graph->TopologicalSort();

        std::map<Tensor*, TensorLifetime> lifetimes;
        int step = 0;

        for (auto* node : sorted_nodes) {
            // 输入张量的死亡时刻是当前步骤
            for (auto* input : node->inputs_) {
                if (lifetimes.find(input) != lifetimes.end()) {
                    lifetimes[input].death_step = step;
                }
            }

            // 输出张量的诞生时刻是当前步骤
            for (auto* output : node->outputs_) {
                lifetimes[output] = {
                    output,
                    step,
                    -1,  // 尚未死亡
                    output->NumElements() * sizeof(float)
                };
            }

            step++;
        }

        // 输出张量在最后死亡
        for (auto& [tensor, lifetime] : lifetimes) {
            if (lifetime.death_step == -1) {
                lifetime.death_step = step;
            }
        }

        std::vector<TensorLifetime> result;
        for (auto& [tensor, lifetime] : lifetimes) {
            result.push_back(lifetime);
        }
        return result;
    }

    /**
     * 构建内存复用计划
     * 使用首次适应算法
     */
    struct MemoryBlock {
        size_t offset;
        size_t size;
        std::vector<Tensor*> tensors;
    };

    std::vector<MemoryBlock> BuildReusePlan(
            const std::vector<TensorLifetime>& lifetimes) {
        // 按诞生时间排序
        auto sorted = lifetimes;
        std::sort(sorted.begin(), sorted.end(),
            [](const auto& a, const auto& b) {
                return a.birth_step < b.birth_step;
            });

        std::vector<MemoryBlock> blocks;

        for (const auto& lifetime : sorted) {
            // 寻找可复用的块
            bool reused = false;
            for (auto& block : blocks) {
                if (CanReuse(block, lifetime)) {
                    block.tensors.push_back(lifetime.tensor);
                    reused = true;
                    break;
                }
            }

            // 如果没有可复用的块,分配新块
            if (!reused) {
                blocks.push_back({0, lifetime.size, {lifetime.tensor}});
            }
        }

        return blocks;
    }

    /**
     * 判断是否可以复用内存块
     */
    bool CanReuse(const MemoryBlock& block,
                  const TensorLifetime& lifetime) {
        // 检查大小
        if (lifetime.size > block.size) {
            return false;
        }

        // 检查生命周期是否重叠
        for (auto* tensor : block.tensors) {
            if (lifetimes_overlap(*tensor, *lifetime.tensor)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 分配内存
     */
    void AllocateMemory(ComputeGraph* graph,
                       const std::vector<MemoryBlock>& plan) {
        size_t current_offset = 0;

        for (const auto& block : plan) {
            // 为块分配内存
            for (auto* tensor : block.tensors) {
                tensor->memory_offset = current_offset;
            }

            current_offset += block.size;
        }

        // 设置总内存大小
        graph->total_memory_size_ = current_offset;
    }
};

四、多流并行执行

4.1 流管理器

/**
 * 流管理器
 * 管理多个流的创建、同步和任务调度
 */
class StreamManager {
public:
    StreamManager(int num_streams = 4)
        : num_streams_(num_streams) {
        // 创建流
        streams_.resize(num_streams_);
        for (int i = 0; i < num_streams_; ++i) {
            aclrtCreateStream(&streams_[i]);
        }
    }

    ~StreamManager() {
        // 销毁流
        for (auto stream : streams_) {
            aclrtDestroyStream(stream);
        }
    }

    /**
     * 获取流(负载均衡)
     */
    aclrtStream GetStream() {
        int stream_id = current_stream_ % num_streams_;
        current_stream_++;
        return streams_[stream_id];
    }

    /**
     * 同步所有流
     */
    void SyncAll() {
        for (auto stream : streams_) {
            aclrtSynchronizeStream(stream);
        }
    }

    /**
     * 在两个流之间创建依赖关系
     */
    void AddDependency(int from_stream, int to_stream) {
        aclrtEvent event;
        aclrtCreateEvent(&event);

        // 在 from_stream 中记录事件
        aclrtRecordEvent(event, streams_[from_stream]);

        // 在 to_stream 中等待事件
        aclrtStreamWaitEvent(streams_[to_stream], event);

        dependencies_[to_stream].push_back(event);
    }

private:
    int num_streams_;
    std::vector<aclrtStream> streams_;
    int current_stream_ = 0;
    std::map<int, std::vector<aclrtEvent>> dependencies_;
};

4.2 任务调度器

/**
 * 任务调度器
 * 负责将图节点调度到不同的流上执行
 */
class TaskScheduler {
public:
    /**
     * 调度图执行
     */
    void Schedule(ComputeGraph* graph, StreamManager* manager) {
        // 1. 分析图的并行性
        auto parallel_groups = AnalyzeParallelism(graph);

        // 2. 为每个并行组分配流
        for (auto& group : parallel_groups) {
            int stream_id = AssignStream(group, manager);
            group.stream_id = stream_id;
        }

        // 3. 处理组间依赖
        HandleDependencies(parallel_groups, manager);

        // 4. 生成执行计划
        execution_plan_ = GenerateExecutionPlan(parallel_groups);
    }

    /**
     * 执行计划
     */
    void Execute() {
        for (auto& task : execution_plan_) {
            // 在指定的流上执行任务
            ExecuteTask(task);
        }
    }

private:
    /**
     * 并行组
     * 可以并行执行的节点集合
     */
    struct ParallelGroup {
        std::vector<OpNode*> nodes;
        int stream_id;
        std::vector<int> dependencies;
    };

    /**
     * 分析图的并行性
     * 使用层次化调度算法
     */
    std::vector<ParallelGroup> AnalyzeParallelism(ComputeGraph* graph) {
        std::vector<ParallelGroup> groups;

        // 计算每个节点的深度(从输入到该节点的最长路径)
        auto depths = ComputeDepths(graph);

        // 按深度分组
        std::map<int, std::vector<OpNode*>> depth_groups;
        for (auto* node : graph->nodes_) {
            depth_groups[depths[node]].push_back(node);
        }

        // 为每个深度创建一个并行组
        for (auto& [depth, nodes] : depth_groups) {
            ParallelGroup group;
            group.nodes = nodes;
            groups.push_back(std::move(group));
        }

        return groups;
    }

    /**
     * 计算节点深度
     */
    std::map<OpNode*, int> ComputeDepths(ComputeGraph* graph) {
        std::map<OpNode*, int> depths;

        // 拓扑排序
        auto sorted = graph->TopologicalSort();

        for (auto* node : sorted) {
            int max_input_depth = -1;

            for (auto* input : node->inputs_) {
                if (input->producer) {
                    max_input_depth = std::max(
                        max_input_depth,
                        depths[input->producer]
                    );
                }
            }

            depths[node] = max_input_depth + 1;
        }

        return depths;
    }

    /**
     * 为并行组分配流
     */
    int AssignStream(ParallelGroup& group, StreamManager* manager) {
        // 简单策略:轮询分配
        static int next_stream = 0;
        int stream_id = next_stream;
        next_stream = (next_stream + 1) % manager->GetNumStreams();
        return stream_id;
    }

    /**
     * 处理组间依赖
     */
    void HandleDependencies(std::vector<ParallelGroup>& groups,
                           StreamManager* manager) {
        for (size_t i = 1; i < groups.size(); ++i) {
            // 当前组依赖于前一组
            auto& prev_group = groups[i - 1];
            auto& curr_group = groups[i];

            curr_group.dependencies.push_back(prev_group.stream_id);

            // 创建流间依赖
            manager->AddDependency(
                prev_group.stream_id,
                curr_group.stream_id
            );
        }
    }

    /**
     * 生成执行计划
     */
    struct Task {
        OpNode* node;
        int stream_id;
        std::vector<aclrtEvent> wait_events;
    };

    std::vector<Task> GenerateExecutionPlan(
            const std::vector<ParallelGroup>& groups) {
        std::vector<Task> plan;

        for (const auto& group : groups) {
            for (auto* node : group.nodes) {
                Task task;
                task.node = node;
                task.stream_id = group.stream_id;
                // task.wait_events = ...
                plan.push_back(task);
            }
        }

        return plan;
    }

    std::vector<Task> execution_plan_;
};

五、使用示例

5.1 模型加载与编译

/**
 * GE 模型加载与编译示例
 */
class GEModelLoader {
public:
    /**
     * 加载 ONNX 模型
     */
    void LoadONNX(const std::string& model_path) {
        // 1. 解析 ONNX 模型
        auto onnx_model = ParseONNX(model_path);

        // 2. 构建 GE 计算图
        graph_ = BuildGraph(onnx_model);

        // 3. 运行优化 Pass
        OptimizeGraph();

        // 4. 编译图
        CompileGraph();
    }

    /**
     * 加载 PyTorch 模型
     */
    void LoadTorchScript(const std::string& model_path) {
        // 1. 加载 TorchScript 模型
        auto torch_model = torch::jit::load(model_path);

        // 2. 转换为 GE 图
        graph_ = ConvertTorchToGE(torch_model);

        // 3. 优化与编译
        OptimizeGraph();
        CompileGraph();
    }

    /**
     * 推理
     */
    std::vector<Tensor> Infer(const std::vector<Tensor>& inputs) {
        // 1. 准备输入
        PrepareInputs(inputs);

        // 2. 执行图
        executor_->Execute();

        // 3. 获取输出
        return GetOutputs();
    }

private:
    void OptimizeGraph() {
        // 创建优化管理器
        PassManager pass_manager;

        // 添加各种 Pass
        pass_manager.AddPass(std::make_shared<ConstantFoldingPass>());
        pass_manager.AddPass(std::make_shared<FusionEngine>());
        pass_manager.AddPass(std::make_shared<MemoryOptimizer>());
        pass_manager.AddPass(std::make_shared<LayoutTransformPass>());

        // 运行优化
        pass_manager.Run(graph_.get());
    }

    void CompileGraph() {
        // 创建编译器
        GraphCompiler compiler;

        // 编译图
        auto compiled_graph = compiler.Compile(graph_.get());

        // 创建执行器
        executor_ = std::make_unique<GraphExecutor>(compiled_graph);
    }

    std::shared_ptr<ComputeGraph> graph_;
    std::unique_ptr<GraphExecutor> executor_;
};

5.2 自定义算子注册

/**
 * 自定义算子注册
 */
REGISTER_OP("MyCustomOp")
    .Input("input", TensorType::FLOAT)
    .Output("output", TensorType::FLOAT)
    .Attr("alpha", float, 1.0f)
    .SetKernel([](OpContext* ctx) {
        auto* input = ctx->GetInput(0);
        auto* output = ctx->GetOutput(0);
        float alpha = ctx->GetAttr<float>("alpha");

        // 实现:output = alpha * input
        for (size_t i = 0; i < input->NumElements(); ++i) {
            output->data[i] = alpha * input->data[i];
        }

        return ACL_SUCCESS;
    });

/**
 * 使用自定义算子构建图
 */
void BuildGraphWithCustomOp() {
    auto graph = std::make_shared<ComputeGraph>("MyGraph");

    // 创建自定义算子节点
    auto custom_op = std::make_shared<OpNode>();
    custom_op->type_ = "MyCustomOp";
    custom_op->name_ = "my_custom_op";
    custom_op->attrs_["alpha"] = 2.0f;

    // 设置输入输出
    custom_op->inputs_ = {input_tensor};
    custom_op->outputs_ = {output_tensor};

    // 添加到图
    graph->AddNode(custom_op);
}

六、性能对比

6.1 优化效果

模型 优化前耗时 优化后耗时 加速比 内存节省
ResNet-50 8.5ms 2.1ms 4.0x 35%
BERT-Base 45ms 12ms 3.8x 28%
GPT-2 (Small) 180ms 52ms 3.5x 22%

6.2 多流并行效果

模型 单流吞吐 4流吞吐 加速比
ResNet-50 280 img/s 980 img/s 3.5x
BERT-Base 45 seq/s 158 seq/s 3.5x

七、总结

GE 作为 CANN 的图引擎,提供了强大的图优化和执行能力。通过算子融合、内存优化、多流并行等技术,显著提升了模型在 NPU 上的执行效率。

7.1 核心优势

  1. 深度优化: 多种图优化技术,充分发挥硬件能力
  2. 高效执行: 多流并行,充分利用硬件资源
  3. 框架支持: 支持主流 AI 框架
  4. 易于扩展: 灵活的插件机制,便于定制

7.2 相关链接

  • CANN组织: https://atomgit.com/cann
  • ge仓库: https://atomgit.com/cann/ge
  • metadef (图定义): https://atomgit.com/cann/metadef
  • graph-autofusion (自动融合): https://atomgit.com/cann/graph-autofusion
  • runtime (运行时): https://atomgit.com/cann/runtime

本文档基于 CANN 开源项目编写,展示了 GE 图引擎的核心功能和使用方法。更多详细信息请参考官方文档和源代码。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐