From 0af644fb4f99664fc8cc9906b0c863b0a26deeae Mon Sep 17 00:00:00 2001 From: FishQ Date: Tue, 2 Sep 2025 14:17:17 +0000 Subject: [PATCH] update README.md. Signed-off-by: FishQ --- README.md | 116 ++++++++++++++++++++++++++++--------------- docs/media/image.png | Bin 0 -> 22465 bytes 2 files changed, 77 insertions(+), 39 deletions(-) create mode 100644 docs/media/image.png diff --git a/README.md b/README.md index ad2fc889..e37d4c53 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,22 @@ # Ascend Transformer Boost ## 介绍 -Ascend Transformer Boost加速库(下文简称为ATB加速库)是一款高效、可靠的加速库,基于华为Ascend AI处理器,专门为Transformer模型的训练和推理而设计。 +Ascend Transformer Boost加速库(下文简称为ATB加速库)是一款基于华为Ascend AI处理器,专门为Transformer模型的训练和推理而设计的加速库。主要包含一系列Transformer模型算子和运行时优化措施, 提高模型性能。 -ATB加速库采用了一系列优化策略,包括算法优化、硬件优化和软件优化,能够显著提升Transformer模型的训练和推理速度,同时降低能耗和成本。具体来说,ATB加速库通过优化矩阵乘法等核心算子和注意力机制的实现方式,实现了对Transformer模型的高效加速。此外,ATB加速库还充分利用了Ascend AI处理器的硬件特性,如算力、存储带宽和内存带宽,通过硬件加速和数据重用等技术,进一步提升了性能和效率。ATB加速库目前提供了底层基础的高性能算子以及高效的算子组合技术(Graph图算子),同时上层支持对接多种模型框架如PyTorch、MindSpore、Paddle。 +高性能算子:针对昇腾系列芯片, 提供高度优化的Transformer算子, 包括Linear/softmax/PageAttention/RingAttention等。 +组图能力: 支持通过手动构图方式将上述算子或用户自定义算子构建成图算子,用于表征Layer或Model。 +运行时优化: 通过算子间内存复用和算子下发顺序优化, 提升整体模型性能。 +Plugin: 提供Plugin能力, 支持用户使用第三方算子组图。 -总而言之,ATB加速库中包含了各类Transformer类模型的高度优化模块,在各种应用场景中发挥重要作用,为模型的训练和推理提供了强有力的支持。 +## 软件架构 +加速库接能主要分成三部分: +![输入图片说明](docs/media/image.png) -## 软件架构 -加速库接口功能主要分成三部分: -- 提供基础原生的算子(Operation),用户可以根据需求使用对应的算子完成计算功能。 -- 提供图算子机制,用户根据模型设计对应的图算子,使用加速库提供的原生算子和创建的自定义算子创建图算子,完成相应的计算。 -- 提供插件(Plugin)机制,用户可以根据自己的需求创建自定义的算子。 +ATB的架构大致可以分成3层: +1. 接口层: 提供单算子接口、组图接口、PlugIn算子接入接口三类 +2. 功能实现层:包括算子实现、运行时优化、Context管理等 +3. 算子运行时:主要提供算子分包、下发等功能。 ## 环境构建 - [安装CANN环境](https://www.hiascend.com/document/detail/zh/canncommercial/80RC22/softwareinst/instg/instg_0001.html?Mode=PmIns&OS=Ubuntu&Software=cannToolKit) @@ -22,28 +26,19 @@ ATB加速库采用了一系列优化策略,包括算法优化、硬件优化 ``` ## 使用教程 - - - 加速库编译
+ 用户可以选择从源码编译安装,也可以通过官网下载版本包安装。 下面的安装方式2选1. + 前置条件:已经在环境上安装了对应的CANN ToolKit包。 安装步骤见:https://www.hiascend.com/document/detail/zh/canncommercial/82RC1/softwareinst/instg/instg_quick.html?Mode=PmIns&InstallType=local&OS=Debian&Software=cannToolKit + + - 源码编译并安装
+ 注意: ATB是CANN的标准组件, 提供标准的安装包供用户使用。如果希望使用已经编译好的标准包, 可以跳过这一步骤。 编译加速库,设置加速库环境变量: ```sh > cd ascend-transformer-boost - > bash scripts/build.sh - > source output/atb/set_env.sh + > bash scripts/build.sh # 编译 + > source output/atb/set_env.sh #安装 ``` - - 无法获取ascend-op-common-lib代码仓时,可通过安装nnal软件包获取对应so文件
- - 安装步骤可参考 `run包使用` - - 代码及软件包版本对应关系:
- nnal软件包需保持和toolkit及kernels软件包版本一致 - |CANN|代码分支| - |-|-| - |CANN 8.1.RC1|br_feature_cann_8.2.RC1_0515POC_20250630| - - 执行 - ```sh - source {install path}/nnal/atb/set_env.sh - export ATB_BUILD_DEPENDENCY_PATH=${ATB_HOME_PATH} - ``` - - run包使用
+ - ATN安装使用
- run包获取 1. 进入网址:https://www.hiascend.com/developer/download/commercial 2. 产品系列选择服务器,产品型号根据设备型号选择,选择所需解决方案版本,随后在CANN区域选择软件包跟随指引即可获取相关run包 @@ -51,25 +46,68 @@ ATB加速库采用了一系列优化策略,包括算法优化、硬件优化 其中,{version}表示软件版本号,{arch}表示CPU架构。 - 安装run包(需要依赖cann环境) ```sh - chmod +x 软件包名.run # 增加对软件包的可执行权限 - ./软件包名.run --check # 校验软件包安装文件的一致性和完整性 - ./软件包名.run --install # 安装软件,可使用--help查询相关安装选项 + chmod +x Ascend-cann-nnal__linux-.run # 增加对软件包的可执行权限 + ./Ascend-cann-nnal__linux-.run --check # 校验软件包安装文件的一致性和完整性 + ./Ascend-cann-nnal__linux-.run --install --whitelist=atb # 安装软件,可使用--help查询相关安装选项 ``` 出现提示`xxx install success!`则安装成功 ## 使用说明 -- 加速库算子单元测试 - - c++ - ```sh - bash scripts/build.sh unittest +- 单算子调用 ``` - - python - ```sh - bash scripts/build.sh pythontest - ``` - - csv - ```sh - bash scripts/build.sh csvopstest + #include + #include + + int deviceId = 0; + aclError status = aclrtSetDevice(deviceId); + // 以elewise大类中的Add算子为例,可通过以下方式构造对应参数: + atb::infer::ElewiseParam param; + param.elewiseType = atb::infer::ElewiseParam::ELEWISE_ADD; + // 创建算子对象实例 + atb::Operation *op = nullptr; + atb::Status st = atb::CreateOperation(param, &op); + + // Tensor构造方法 + atb::Tensor a; + a.desc.dtype = ACL_FLOAT16; // 配置Tensor数据类型 + a.desc.format = ACL_FORMAT_ND; // 配置Tensor格式 + a.desc.shape.dimNum = 2; // 配置Tensor维度数 + a.desc.shape.dims[0] = 3; // 配置Tensor第0维大小 + a.desc.shape.dims[1] = 3; // 配置Tensor第1维大小 + a.dataSize = Utils::GetTensorSize(a); // 获取Tensor内存大小 + status = aclrtMalloc(&a.deviceData, a.dataSize, ACL_MEM_MALLOC_HUGE_FIRST); // 申请device内存 + // 按上述方法构造所有输入和输出tensor,存入VariantPack + atb::VariantPack variantPack; + variantPack.inTensors = { a, ... }; + variantPack.outTensors = { output, ... }; + + atb::Context *context = nullptr; + st = atb::CreateContext(&context); + aclrtStream stream = nullptr; + status = aclrtCreateStream(&stream); + context->SetExecuteStream(stream); + + // 调用Setup接口,计算workspace大小。 + uint64_t workspaceSize = 0; + st = op->Setup(variantPack, workspaceSize, context); + + // 根据workspace大小申请NPU内存。 + void *workspace = nullptr; + status = aclrtMalloc(&workspace, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); + + // 调用Execute接口,执行算子。 + st = op->Execute(variantPack, (uint8_t *)workspace, workspaceSize, context); + + // 销毁创建的对象,释放内存。 + status = aclrtDestroyStream(stream); // 销毁stream + status = aclrtFree(workspace); // 销毁workspace + st = atb::DestroyOperation(op); // 销毁op对象 + st = atb::DestroyContext(context); // 销毁context + // 下面代码为释放Tensor的示例代码,实际使用时需释放VariantPack中的所有Tensor + status = aclrtFree(tensor.deviceData); + tensor.deviceData = nullptr; + tensor.dataSize = 0; + ``` ## 参与贡献 diff --git a/docs/media/image.png b/docs/media/image.png new file mode 100644 index 0000000000000000000000000000000000000000..3a9ac993908d7062ebda7ab54c55df03614a74f4 GIT binary patch literal 22465 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 RxED@>B5kL76?yJC_a`5UK@b1{ literal 0 HcmV?d00001 -- Gitee