尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

昇腾AscendC核函数直调:CMake单文件main实战模板

昇腾AscendC核函数直调:CMake单文件main实战模板 1. 项目概述为什么一个“单文件 main”能成为昇腾开发者的救命稻草昇腾生态里写核函数不是最难的难的是让核函数真正跑起来——不是在模型训练框架里被层层封装调用而是亲手把它从内存加载、配置上下文、分配设备内存、启动执行、同步等待、释放资源一气呵成地串起来。我见过太多工程师卡在aclrtLaunch报错ACL_ERROR_INVALID_ARGS或ACL_ERROR_RT_MODEL_EXECUTION_FAILED上翻遍文档找不到到底是stream没创建对还是args地址没传准抑或workspace大小算错了。问题不在于不会写 AscendC而在于缺乏一个最小可运行、零抽象干扰、所有环节裸露可见的工程模板。这个标题里的“CMake aclrtLaunch 单文件 main 完整示例”就是为解决这个痛点而生的。它不是教你如何写高性能核函数而是给你一把“手术刀”把昇腾驱动层、ACL运行时、AscendC编译产物、Host端调度逻辑全部摊开在同一个.cpp文件里用最朴素的 C 和标准 CMake 构建流程组织起来。关键词昇腾、AscendC、CMake、aclrtLaunch、核函数全部落在实处——昇腾是硬件底座AscendC 是编程语言CMake 是构建粘合剂aclrtLaunch 是执行入口核函数是最终目标。它适合三类人刚从 CUDA 转过来想快速建立昇腾执行模型的开发者需要调试核函数底层行为比如 shared memory 使用、block 同步效果的性能工程师以及正在搭建私有推理 pipeline、必须绕过 PyTorch/TensorFlow 封装直接对接 ACL 的系统集成者。它不承诺“一键部署”但保证你改完一行代码就能看到aclrtLaunch返回值而不是等半小时模型训练日志滚屏后才发现是aclrtSetDevice没调成功。我第一次用这个模板时是在调试一个矩阵乘法核函数的 bank conflict 问题。传统方式得先编译成 om 模型再用aclmdlLoadFromFile加载中间经过图优化、算子融合根本看不到原始核函数的执行轨迹。而这个单文件模板让我直接把.o文件当二进制 blob 加载用aclrtMemcpy手动拷贝输入输出aclrtSynchronizeStream精确卡点测时甚至能在aclrtLaunch前后插桩打印aclrtGetRunMode和aclrtGetContext确认当前是否处于ACL_HOST模式。这种“剥洋葱式”的可控性是任何高级框架都给不了的。它不是替代框架而是框架的“显微镜”。2. 整体设计思路与方案选型逻辑为什么必须是 CMake为什么拒绝 Python 封装2.1 为什么坚持“单文件 main”——对抗抽象泄漏的必然选择很多初学者会疑惑为什么不直接用昇腾官方的sample目录下的工程那些工程结构清晰、分层合理看着更“专业”。但恰恰是这种“专业”成了调试障碍。官方 sample 通常把 Host 侧逻辑拆成main.cpp、utils.cpp、acl_helper.cpp等多个文件又引入acl.json配置、model目录管理、data目录预置甚至集成opencv做图像预处理。当你遇到aclrtLaunch失败时问题可能出在utils.cpp里某个aclrtMalloc返回了nullptr却没检查acl_helper.cpp中aclrtCreateStream创建的 stream 被意外释放或者acl.json里device_id配置和实际物理卡号不一致。你得在五个文件间跳转还得查 JSON 解析逻辑最后发现 bug 在utils.cpp第 87 行一个未初始化的指针。单文件 main 的核心价值是强制线性化执行流。整个生命周期初始化 ACL → 设置 Device → 创建 Context/Stream → 分配 Device 内存 → 拷贝 Host 数据 → 加载核函数二进制 → 构造 launch 参数 → 调用aclrtLaunch→ 同步等待 → 拷回结果 → 释放资源 → 反初始化 ACL全部写在一个main()函数里按顺序从上到下执行。没有跨文件依赖没有隐式状态传递没有配置文件解析。aclrtLaunch的前一个语句是什么后一个语句是什么一目了然。我把它称为“原子调试单元”——你可以用gdb在aclrtLaunch行下断点step into直接进入 ACL 库内部如果符号可用或者print所有参数地址和值确认funcDesc是否有效、args数组是否对齐、workSpace大小是否足够。这种确定性是复杂工程无法提供的。提示这不是反对工程化而是明确阶段分工。日常开发用模块化工程但当你需要定位一个ACL_ERROR_RT_MEMORY_ALLOCATION_FAILED错误时请立刻切到单文件模板把所有变量声明、内存分配、参数构造逻辑复制过去用最简路径复现问题。我团队有个规矩所有 ACL 相关 issue 提交前必须先在单文件模板里复现否则不予受理。2.2 为什么选 CMake 而非 Makefile 或 MSBuild——跨平台构建的隐形门槛昇腾开发环境横跨 EulerOS华为云、Ubuntu 20.04/22.04本地服务器、甚至 Windows WSL2部分开发者偏好。有人会说“直接写个 shell 脚本调用g不就行了吗” 短期看可以长期必崩。原因有三第一库路径硬编码灾难。昇腾 ACL 库位置随安装方式变化通过install.sh安装在/usr/local/Ascend/ascend-toolkit/latest/通过 Docker 镜像可能在/usr/local/Ascend/通过 conda 安装则在$CONDA_PREFIX/lib/。CMake 的find_package(ACL)或find_library能自动探测并缓存路径而 shell 脚本每次都要export LD_LIBRARY_PATH/usr/local/Ascend/ascend-toolkit/latest/lib64:$LD_LIBRARY_PATH极易遗漏或冲突。第二编译器与标准兼容性。昇腾要求 C14 或更高且需链接libacl.so、libascendcl.so、libruntime.so等多个动态库。CMake 的target_link_libraries(myapp PRIVATE acl ascendcl runtime)会自动处理依赖传递和链接顺序而手动g -lascendcl -lacl -lruntime可能因顺序错误导致 undefined reference。更关键的是昇腾工具链推荐使用g (GCC) 7.3.0或9.3.0CMake 的set(CMAKE_CXX_STANDARD 14)和set(CMAKE_CXX_STANDARD_REQUIRED ON)能强制统一标准避免std::shared_ptr在不同标准下 ABI 不兼容。第三增量构建与依赖管理。核函数.o文件由ascendcc编译器生成其修改时间戳独立于 Host 代码。CMake 的add_custom_command可以精确声明main.cpp依赖于kernel.o当核函数源码变更时只重编 Host 侧反之亦然。而纯 shell 脚本很难优雅实现这种细粒度依赖。注意CMakeLists.txt 里绝不能写include_directories(/usr/local/Ascend/ascend-toolkit/latest/include)这种绝对路径。正确做法是用find_path(ACL_INCLUDE_DIR NAMES acl/acl.h PATHS /usr/local/Ascend/ascend-toolkit/latest/include /usr/local/Ascend/include)然后target_include_directories(myapp PRIVATE ${ACL_INCLUDE_DIR})。这样模板才能在不同环境一键复用。2.3 为什么直调aclrtLaunch而非aclrtLaunchKernel——贴近硬件执行的本质昇腾 ACL 文档里有两个相似接口aclrtLaunch和aclrtLaunchKernel。初看几乎一样参数列表高度重合。但它们的定位截然不同aclrtLaunchKernel是为模型推理场景设计的。它假设你已通过aclmdlLoadFromFile加载了一个完整的 OM 模型文件该模型内部已包含所有核函数定义、输入输出 tensor 描述、workspace 需求等元信息。aclrtLaunchKernel的kernelName参数其实是 OM 模型中某个算子的 nameACL 会自动从模型中提取对应核函数并配置。aclrtLaunch是为核函数直调场景设计的。它接收一个裸的aclrtKernelDesc结构体里面明确指定核函数二进制地址funcDesc、参数数组args、参数个数argsNum、workspace 大小workSpace、streamstream。它不关心模型只关心“这段机器码按这个参数扔到这个 stream 上执行”。本模板选择aclrtLaunch是因为它完全暴露了核函数执行的底层契约。你需要自己计算 workspace 大小aclrtGetWorkspaceSize自己构造 args 数组void* args[5] {d_a, d_b, d_c, m, n}自己确保所有 device 地址已通过aclrtMalloc分配。这个过程强迫你理解workspace 不是随便给个 1MB 就行它取决于核函数内__shared__内存声明和 register usageargs 数组里每个元素必须是void*类型即使你传的是int*也得强转stream必须是aclrtCreateStream创建的有效句柄不能是nullptr或已销毁的 stream。我曾帮一个客户排查ACL_ERROR_RT_KERNEL_LAUNCH_FAILED最终发现他们用aclrtLaunchKernel传入了一个自定义核函数名但 OM 模型里根本没有这个 kernelACL 返回了模糊错误。换成aclrtLaunch后错误立刻变成ACL_ERROR_INVALID_ARGS配合aclrtGetLastError打印出具体哪条 arg 无效5 分钟定位到args[2]是 host 地址而非 device 地址。直调不是为了炫技而是为了获得精准的错误反馈。3. 核心细节解析与实操要点从 AscendC 编译到aclrtLaunch参数构造的全链路拆解3.1 AscendC 核函数编写与编译.o文件才是真正的“可执行体”AscendC 核函数不是.so或.dll而是一个符合特定 ABI 的 ELF object 文件.o。它的编译流程与传统 C 截然不同必须用昇腾专属的ascendcc编译器。假设你有一个矩阵乘法核函数matmul.cc#include common.h #include cube.h extern C __global__ void matmul_kernel(float* a, float* b, float* c, int m, int n, int k) { // ... 实际核函数逻辑使用 cube::gemm 等算子 }编译命令不是g -c而是ascendcc -c -o matmul.o matmul.cc --chipAscend310P --archascend310p --input-formatfp32 --output-formatfp32这里的关键参数--chipAscend310P指定目标芯片型号必须与你的物理卡一致Ascend910B、Ascend310P 等。填错会导致.o文件无法加载。--archascend310p指定指令集架构通常与 chip 对应但需查阅《AscendC 编程指南》确认。ascend310p和ascend910b的寄存器布局不同。--input-formatfp32声明输入数据格式影响编译器生成的 load/store 指令。若实际传入 fp16 数据却声明 fp32运行时会读取错误内存。编译后得到matmul.o它不是一个链接后的可执行文件而是一个位置无关的、包含核函数机器码和元数据的 object 文件。你可以用file matmul.o确认它是ELF 64-bit LSB relocatable, ARM aarch64用readelf -a matmul.o | grep Symbol table查看是否导出了matmul_kernel符号。如果符号表为空说明extern C或__global__声明有误aclrtLaunch会因找不到函数名而失败。实操心得ascendcc编译失败时错误信息往往比g更晦涩。常见坑包括头文件路径未加-I如-I$ASCEND_HOME/ascend-toolkit/latest/ai_core/inc#include common.h中的宏定义未正确定义如__ACL_ARCH_ASCEND310P__核函数参数类型不支持AscendC 目前不支持std::vector只能用原始指针长度。我的习惯是先写一个最简void test_kernel() {}编译通过再逐步添加逻辑。3.2 Host 侧main.cpp关键结构从aclrtSetDevice到aclrtLaunch的七步生死线单文件main.cpp的骨架本质是 ACL 运行时 API 的线性调用序列。每一行都可能成为失败点必须严格遵循顺序#include acl/acl.h #include iostream #include vector #include fstream int main() { // Step 1: 初始化 ACL 运行时 aclError ret aclInit(nullptr); if (ret ! ACL_SUCCESS) { std::cerr aclInit failed, ret ret std::endl; return -1; } // Step 2: 设置当前进程使用的 Device ID物理卡号 ret aclrtSetDevice(0); // 假设用第 0 张卡 if (ret ! ACL_SUCCESS) { std::cerr aclrtSetDevice failed, ret ret std::endl; aclShutdown(); return -1; } // Step 3: 创建 Context 和 Stream aclrtContext context; ret aclrtCreateContext(context, 0); if (ret ! ACL_SUCCESS) { std::cerr aclrtCreateContext failed, ret ret std::endl; aclrtResetDevice(0); aclShutdown(); return -1; } aclrtStream stream; ret aclrtCreateStream(stream); if (ret ! ACL_SUCCESS) { std::cerr aclrtCreateStream failed, ret ret std::endl; aclrtDestroyContext(context); aclrtResetDevice(0); aclShutdown(); return -1; } // Step 4: 分配 Device 内存 size_t size_a m * k * sizeof(float); void* d_a; ret aclrtMalloc(d_a, size_a, ACL_MEM_MALLOC_HUGE_FIRST); if (ret ! ACL_SUCCESS) { std::cerr aclrtMalloc d_a failed, ret ret std::endl; goto cleanup; } // ... 同样分配 d_b, d_c // Step 5: 拷贝 Host 数据到 Device std::vectorfloat h_a(m * k, 1.0f); ret aclrtMemcpy(d_a, size_a, h_a.data(), size_a, ACL_MEMCPY_HOST_TO_DEVICE); if (ret ! ACL_SUCCESS) { std::cerr aclrtMemcpy d_a failed, ret ret std::endl; goto cleanup; } // ... 同样拷贝 h_b, h_c // Step 6: 加载核函数二进制并构造 launch 参数 std::ifstream kernel_file(matmul.o, std::ios::binary | std::ios::ate); std::streamsize size kernel_file.tellg(); kernel_file.seekg(0, std::ios::beg); std::vectorchar kernel_data(size); kernel_file.read(kernel_data.data(), size); aclrtKernelDesc kernel_desc; ret aclrtCreateKernel(kernel_desc, kernel_data.data(), size, matmul_kernel, nullptr); if (ret ! ACL_SUCCESS) { std::cerr aclrtCreateKernel failed, ret ret std::endl; goto cleanup; } // 计算 workspace 大小 size_t workSpaceSize; ret aclrtGetWorkspaceSize(kernel_desc, workSpaceSize); if (ret ! ACL_SUCCESS) { std::cerr aclrtGetWorkspaceSize failed, ret ret std::endl; goto cleanup; } void* workSpace; ret aclrtMalloc(workSpace, workSpaceSize, ACL_MEM_MALLOC_HUGE_FIRST); if (ret ! ACL_SUCCESS) { std::cerr aclrtMalloc workSpace failed, ret ret std::endl; goto cleanup; } // 构造 args 数组必须是 void* 数组按核函数参数顺序排列 void* args[6] {d_a, d_b, d_c, m, n, k}; // 注意标量参数要取地址 // Step 7: 执行核函数 ret aclrtLaunch(kernel_desc, args, 6, workSpace, workSpaceSize, stream); if (ret ! ACL_SUCCESS) { std::cerr aclrtLaunch failed, ret ret std::endl; goto cleanup; } // 同步等待执行完成 ret aclrtSynchronizeStream(stream); if (ret ! ACL_SUCCESS) { std::cerr aclrtSynchronizeStream failed, ret ret std::endl; goto cleanup; } cleanup: // 释放资源省略详细代码 aclrtDestroyKernel(kernel_desc); aclrtFree(d_a); aclrtFree(d_b); aclrtFree(d_c); aclrtFree(workSpace); aclrtDestroyStream(stream); aclrtDestroyContext(context); aclrtResetDevice(0); aclShutdown(); return 0; }这个流程里Step 6 的args构造和workSpace分配是最易出错的环节。args数组里d_a,d_b,d_c是 device 地址void*而m,n,k是 host 端标量的地址int*因为核函数签名是void matmul_kernel(float*, float*, float*, int, int, int)ACL 需要将这些标量值从 host 内存读取并放入核函数的寄存器。如果误传m值而非m地址aclrtLaunch会尝试把整数m当作内存地址去读导致段错误或静默错误。workSpaceSize的计算也常被忽略。AscendC 核函数若使用__shared__ float s_mem[1024]或调用cube::gemm等内置算子都会消耗 workspace。aclrtGetWorkspaceSize必须在aclrtCreateKernel之后、aclrtLaunch之前调用且返回的大小是精确值不能随意放大或缩小。我见过有人为“保险”设为1024*1024结果核函数因 workspace 不足而崩溃也有人用0导致cube::gemm内部 malloc 失败。3.3 CMakeLists.txt 精要如何让 CMake 成为昇腾环境的“智能适配器”一个健壮的CMakeLists.txt不是简单罗列find_package而是主动探测环境并做出决策。以下是核心片段cmake_minimum_required(VERSION 3.10) project(AscendC_Example LANGUAGES CXX) # 1. 探测昇腾工具链根目录 find_path(ASCEND_HOME NAMES ascend-toolkit PATHS /usr/local/Ascend $ENV{ASCEND_HOME} NO_DEFAULT_PATH) if(NOT ASCEND_HOME) message(FATAL_ERROR Ascend toolkit not found. Please set ASCEND_HOME or install to /usr/local/Ascend) endif() # 2. 设置 ACL 库路径和头文件路径 set(ACL_LIB_DIR ${ASCEND_HOME}/ascend-toolkit/latest/lib64) set(ACL_INC_DIR ${ASCEND_HOME}/ascend-toolkit/latest/include) # 3. 查找 ACL 库注意libacl.so 依赖 libascendcl.so 和 libruntime.so find_library(ACL_LIB NAMES acl PATHS ${ACL_LIB_DIR} NO_DEFAULT_PATH) find_library(ASCENDCL_LIB NAMES ascendcl PATHS ${ACL_LIB_DIR} NO_DEFAULT_PATH) find_library(RUNTIME_LIB NAMES runtime PATHS ${ACL_LIB_DIR} NO_DEFAULT_PATH) # 4. 添加可执行文件 add_executable(main main.cpp) # 5. 包含头文件和链接库 target_include_directories(main PRIVATE ${ACL_INC_DIR}) target_link_libraries(main PRIVATE ${ACL_LIB} ${ASCENDCL_LIB} ${RUNTIME_LIB}) # 6. 关键添加核函数 .o 文件作为源文件CMake 会自动处理 add_library(kernel_obj OBJECT IMPORTED) set_property(TARGET kernel_obj PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/matmul.o) target_link_libraries(main PRIVATE kernel_obj) # 7. 设置 C 标准和编译选项 set_property(TARGET main PROPERTY CXX_STANDARD 14) set_property(TARGET main PROPERTY CXX_STANDARD_REQUIRED ON) target_compile_options(main PRIVATE -O2 -Wall -Wextra) # 8. 添加自定义命令当 matmul.cc 变更时自动调用 ascendcc 重新编译 add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/matmul.o COMMAND ${ASCEND_HOME}/ascend-toolkit/latest/compiler/bin/ascendcc -c -o ${CMAKE_CURRENT_SOURCE_DIR}/matmul.o ${CMAKE_CURRENT_SOURCE_DIR}/matmul.cc --chipAscend310P --archascend310p --input-formatfp32 --output-formatfp32 DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/matmul.cc COMMENT Compiling AscendC kernel with ascendcc ) add_custom_target(kernel ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/matmul.o) add_dependencies(main kernel)这个 CMakeLists 的精妙之处在于环境探测find_path(ASCEND_HOME ...)让模板能适应/usr/local/Ascend、$HOME/Ascend、$CONDA_PREFIX等多种安装路径。库依赖显式化target_link_libraries明确列出acl、ascendcl、runtime避免隐式依赖导致的链接失败。核函数集成add_library(kernel_obj OBJECT IMPORTED)将.o文件当作 CMake 的“对象库”导入add_custom_command确保matmul.cc修改后自动触发ascendcc编译。这样make时CMake 会先编译核函数再链接 Host 代码形成真正的增量构建。错误友好message(FATAL_ERROR ...)在找不到昇腾路径时给出明确提示而不是让g报一堆fatal error: acl/acl.h: No such file or directory。注意事项ascendcc编译器路径必须准确。上面示例用了${ASCEND_HOME}/ascend-toolkit/latest/compiler/bin/ascendcc但某些版本可能是${ASCEND_HOME}/compiler/bin/ascendcc。建议在CMakeLists.txt开头加一段探测逻辑find_program(ASCENDCC_COMPILER NAMES ascendcc PATHS ${ASCEND_HOME}/compiler/bin ${ASCEND_HOME}/ascend-toolkit/latest/compiler/bin) if(NOT ASCENDCC_COMPILER) message(FATAL_ERROR ascendcc compiler not found in ASCEND_HOME) endif()这样比硬编码路径更鲁棒。4. 实操过程与核心环节实现从零开始构建、编译、运行的完整 walkthrough4.1 环境准备与依赖安装避开昇腾特有的“软依赖”陷阱昇腾开发环境不是装个 SDK 就完事它有一系列隐性的“软依赖”必须满足否则aclrtLaunch会静默失败或报奇怪错误。我以 Ubuntu 22.04 为例列出必须步骤EulerOS 类似仅路径略有差异第一步安装昇腾驱动和固件# 下载对应版本的驱动包如 Driver-23.0.1-Linux-x86_64.run chmod x Driver-23.0.1-Linux-x86_64.run sudo ./Driver-23.0.1-Linux-x86_64.run --uninstall # 先卸载旧版如有 sudo ./Driver-23.0.1-Linux-x86_64.run --install # 验证dmesg | grep -i ascend 应看到驱动加载成功关键点驱动版本必须与ascend-toolkit版本严格匹配。例如ascend-toolkit-6.5.RC1要求驱动23.0.1。版本错配会导致aclrtSetDevice返回ACL_ERROR_RT_SET_DEVICE_FAILED。第二步安装 Ascend Toolkit# 下载 toolkit 包如 Ascend-toolkit-6.5.RC1-Linux-x86_64.run chmod x Ascend-toolkit-6.5.RC1-Linux-x86_64.run sudo ./Ascend-toolkit-6.5.RC1-Linux-x86_64.run --install # 设置环境变量永久写入 ~/.bashrc echo export ASCEND_HOME/usr/local/Ascend ~/.bashrc echo export LD_LIBRARY_PATH$ASCEND_HOME/ascend-toolkit/latest/lib64:$LD_LIBRARY_PATH ~/.bashrc echo export PATH$ASCEND_HOME/ascend-toolkit/latest/compiler/bin:$PATH ~/.bashrc source ~/.bashrc关键点LD_LIBRARY_PATH必须包含lib64且PATH必须包含compiler/bin否则ascendcc命令不可用。验证ascendcc --version应输出版本号。第三步验证硬件与 ACL 状态# 查看物理卡 npu-smi info # 输出应显示 Ascend310P 或 Ascend910B 的卡号和状态 # 测试 ACL 初始化 cat test_acl.cpp EOF #include acl/acl.h #include iostream int main() { aclError ret aclInit(nullptr); std::cout aclInit: ret std::endl; aclShutdown(); return 0; } EOF g test_acl.cpp -I/usr/local/Ascend/ascend-toolkit/latest/include -L/usr/local/Ascend/ascend-toolkit/latest/lib64 -lacl -o test_acl ./test_acl # 应输出 aclInit: 0关键点如果test_acl输出非零值90% 是LD_LIBRARY_PATH未生效或驱动未加载。此时sudo ldconfig -v | grep ascend应能看到libacl.so被索引。4.2 创建项目目录与文件手把手构建单文件模板现在我们从零开始创建这个模板。打开终端执行mkdir -p ascendc_template cd ascendc_template # 创建 AscendC 核函数 cat matmul.cc EOF #include common.h #include cube.h extern C __global__ void matmul_kernel(float* a, float* b, float* c, int m, int n, int k) { // 简化版逐元素相乘实际应调用 cube::gemm int idx blockIdx.x * blockDim.x threadIdx.x; if (idx m * n) { int row idx / n; int col idx % n; float sum 0.0f; for (int i 0; i k; i) { sum a[row * k i] * b[i * n col]; } c[idx] sum; } } EOF # 创建 Host 主程序 cat main.cpp EOF #include acl/acl.h #include iostream #include vector #include fstream #include chrono int main() { const int m 1024, n 1024, k 1024; size_t size_a m * k * sizeof(float); size_t size_b k * n * sizeof(float); size_t size_c m * n * sizeof(float); // Step 1: Init ACL aclError ret aclInit(nullptr); if (ret ! ACL_SUCCESS) { std::cerr aclInit failed: ret std::endl; return -1; } // Step 2: Set Device ret aclrtSetDevice(0); if (ret ! ACL_SUCCESS) { std::cerr aclrtSetDevice failed: ret std::endl; aclShutdown(); return -1; } // Step 3: Create Context Stream aclrtContext context; ret aclrtCreateContext(context, 0); if (ret ! ACL_SUCCESS) { std::cerr aclrtCreateContext failed: ret std::endl; aclrtResetDevice(0); aclShutdown(); return -1; } aclrtStream stream; ret aclrtCreateStream(stream); if (ret ! ACL_SUCCESS) { std::cerr aclrtCreateStream failed: ret std::endl; aclrtDestroyContext(context); aclrtResetDevice(0); aclShutdown(); return -1; } // Step 4: Malloc Device Memory void* d_a, *d_b, *d_c; ret aclrtMalloc(d_a, size_a, ACL_MEM_MALLOC_HUGE_FIRST); if (ret ! ACL_SUCCESS) goto cleanup; ret aclrtMalloc(d_b, size_b, ACL_MEM_MALLOC_HUGE_FIRST); if (ret ! ACL_SUCCESS) goto cleanup; ret aclrtMalloc(d_c, size_c, ACL_MEM_MALLOC_HUGE_FIRST); if (ret ! ACL_SUCCESS) goto cleanup; // Step 5: Memcpy Host to Device std::vectorfloat h_a(m * k, 1.0f), h_b(k * n, 2.0f); ret aclrtMemcpy(d_a, size_a, h_a.data(), size_a, ACL_MEMCPY_HOST_TO_DEVICE); if (ret ! ACL_SUCCESS) goto cleanup; ret aclrtMemcpy(d_b, size_b, h_b.data(), size_b, ACL_MEMCPY_HOST_TO_DEVICE); if (ret ! ACL_SUCCESS) goto cleanup; // Step 6: Load Kernel Launch std::ifstream kernel_file(matmul.o, std::ios::binary | std::ios::ate); std::streamsize size kernel_file.tellg(); kernel_file.seekg(0, std::ios::beg); std::vectorchar kernel_data(size); kernel_file.read(kernel_data.data(), size); aclrtKernelDesc kernel_desc; ret aclrtCreateKernel(kernel_desc, kernel_data.data(), size, matmul_kernel, nullptr); if (ret ! ACL_SUCCESS) goto cleanup; size_t workSpaceSize; ret aclrtGetWorkspaceSize(kernel_desc, workSpaceSize); if (ret ! ACL_SUCCESS) goto cleanup; void* workSpace; ret aclrtMalloc(workSpace, workSpaceSize, ACL_MEM_MALLOC_HUGE_FIRST); if (ret ! ACL_SUCCESS) goto cleanup; void* args[6] {d_a, d_b, d_c, m, n, k}; auto start std::chrono::high_resolution_clock::now(); ret aclrtLaunch(kernel_desc, args, 6, workSpace, workSpaceSize, stream); if (ret ! ACL_SUCCESS) goto cleanup; ret aclrtSynchronizeStream(stream); if (ret ! ACL_SUCCESS) goto cleanup; auto end std::chrono::high_resolution_clock::now(); auto duration std::chrono::duration_caststd::chrono::microseconds(end - start); std::cout Kernel execution time: duration.count() us std::endl; cleanup: if (workSpace) aclrtFree(workSpace); if (d_a) aclrtFree(d_a); if (d_b) aclrtFree(d_b); if (d_c) aclrtFree(d_c); if (kernel_desc) aclrtDestroyKernel(kernel_desc); if (stream) aclrtDestroyStream(stream); if (context) aclrtDestroyContext(context); aclrtResetDevice(0); aclShutdown(); return 0; } EOF # 创建 CMakeLists.txt cat CMakeLists.txt EOF cmake_minimum_required(VERSION 3.10) project(AscendC_Example LANGUAGES CXX) find_path(ASCEND_HOME NAMES ascend-toolkit PATHS /usr/local/Ascend $ENV{ASCEND_HOME} NO_DEFAULT_PATH) if(NOT ASCEND_HOME) message(FATAL_ERROR Ascend toolkit not found. Please set ASCEND_HOME or install to /usr/local/Ascend) endif() set(ACL_LIB_DIR ${ASCEND_HOME}/ascend-toolkit/latest/lib64) set(ACL_INC_DIR ${ASCEND_HOME}/ascend-toolkit/latest/include) find_library(ACL_LIB NAMES acl PATHS ${
返回列表