【昇腾AI实战】编写第一个AscendCL程序:从初始化到资源释放
本文介绍了如何开发第一个AscendCL程序,包含四个关键步骤:1. 初始化AscendCL运行环境(aclInit);2. 申请设备内存(aclrtMalloc);3. 释放设备内存(aclrtFree);4. 清理运行环境(aclFinalize)。通过创建项目目录、编写CMake配置文件和示例代码,详细说明了AscendCL的基本开发流程,包括错误处理、资源管理和生命周期控制等核心概念。该程
摘要: 理论最终要服务于实践。本文将带领大家动手编写第一个完整的AscendCL程序。我们将从最基本的AscendCL初始化和去初始化入手,并实践Device内存的申请与释放,让你亲身感受AscendCL的开发流程。
- 项目创建与准备
在开始AscendCL编程之前,首先需要创建一个规范的项目目录结构。建议按照以下步骤操作:
(1) 创建项目目录:
mkdir -p ascend_hello_world/src ascend_hello_world/build
cd ascend_hello_world
这里创建了两个子目录:src用于存放源代码,build用于存放编译生成的文件。
(2) 创建源码文件:
touch src/main.c
在src目录下创建主程序文件main.c,这将包含我们的AscendCL示例代码。
(3) 创建编译配置文件:
touch CMakeLists.txt
创建CMake构建脚本,用于配置项目的编译选项和依赖关系。
- 代码实现
下面是一个完整的AscendCL基础程序实现,包含四个关键步骤:
#include "acl/acl.h"
#include <stdio.h>
#define MEM_SIZE (1024)
int main() {
// 1. 初始化AscendCL运行时环境
aclError ret = aclInit(NULL);
if (ret != ACL_SUCCESS) {
printf("aclInit failed, error code = %d\n", ret);
return -1;
}
// 2. 申请Device内存
void* devPtr = NULL;
ret = aclrtMalloc(&devPtr, MEM_SIZE, ACL_MEM_MALLOC_NORMAL_ONLY);
if (ret != ACL_SUCCESS) {
printf("aclrtMalloc failed, error code = %d\n", ret);
aclFinalize();
return -1;
}
printf("Successfully allocated %d bytes on device\n", MEM_SIZE);
// 3. 释放Device内存
ret = aclrtFree(devPtr);
if (ret != ACL_SUCCESS) {
printf("aclrtFree failed, error code = %d\n", ret);
}
// 4. 去初始化AscendCL
ret = aclFinalize();
if (ret != ACL_SUCCESS) {
printf("aclFinalize failed, error code = %d\n", ret);
return -1;
}
return 0;
}
这个示例展示了AscendCL编程的基本流程,详细说明了如何使用AscendCL API进行设备内存管理:
-
环境初始化
首先调用aclInit(const char *configPath)函数初始化运行环境,其中configPath参数可以指定配置文件路径(如nullptr表示使用默认配置)。这是所有AscendCL程序的必要第一步。 -
设备内存分配
使用aclrtMalloc(void **devPtr, size_t size, aclrtMemMallocPolicy policy)在设备上分配内存:devPtr:指向设备内存指针的指针size:要分配的内存大小(示例中通过宏MEM_SIZE定义为1024字节)policy:内存分配策略(如ACL_MEM_MALLOC_HUGE_FIRST)
-
内存释放
完成计算后,通过aclrtFree(void *devPtr)释放设备内存,传入之前分配的内存指针。 -
资源清理
最后调用aclFinalize()清理所有分配的资源和运行环境。 -
错误处理
每个API调用后都通过检查返回状态(如ACL_ERROR_NONE)确保操作成功执行,这是开发可靠应用的必备实践。例如:aclError ret = aclrtMalloc(&devPtr, MEM_SIZE, ACL_MEM_MALLOC_HUGE_FIRST); if (ret != ACL_ERROR_NONE) { // 错误处理逻辑 }
该流程适用于各种AI计算场景,如图像处理、模型推理等需要设备内存管理的场合。合理的内存分配策略(如HUGE_FIRST)可以优化内存使用效率。
代码实现详解
基础程序框架
#include "acl/acl.h" // AscendCL核心头文件
#include <stdio.h> // 标准输入输出
#define MEM_SIZE (1024) // 定义内存分配大小
int main() {
// 1. 初始化AscendCL运行时环境
aclError ret = aclInit(NULL);
if (ret != ACL_SUCCESS) {
printf("aclInit failed, error code = %d\n", ret);
return -1;
}
// 2. 申请Device内存
void* devPtr = NULL;
ret = aclrtMalloc(&devPtr, MEM_SIZE, ACL_MEM_MALLOC_NORMAL_ONLY);
if (ret != ACL_SUCCESS) {
printf("aclrtMalloc failed, error code = %d\n", ret);
aclFinalize();
return -1;
}
printf("Successfully allocated %d bytes on device\n", MEM_SIZE);
// 3. 释放Device内存
ret = aclrtFree(devPtr);
if (ret != ACL_SUCCESS) {
printf("aclrtFree failed, error code = %d\n", ret);
}
// 4. 去初始化AscendCL
ret = aclFinalize();
if (ret != ACL_SUCCESS) {
printf("aclFinalize failed, error code = %d\n", ret);
return -1;
}
return 0;
}
关键API详解
增强版示例
#include <stdio.h>
#include "acl/acl.h"
#define MEMORY_SIZE (100) // 定义内存大小常量
int main() {
// 初始化
const char *aclConfigPath = ""; // 空字符串表示默认配置
aclError ret = aclInit(aclConfigPath);
if (ret != ACL_SUCCESS) {
printf("AscendCL init failed, error code = %d\n", ret);
return -1;
}
printf("AscendCL init success!\n");
// 1. 指定运算的Device
ret = aclrtSetDevice(0); // 使用第0号Device
if (ret != ACL_SUCCESS) {
printf("Set Device failed, error code = %d\n", ret);
aclFinalize();
return -1;
}
// 2. 申请Device内存
void* devPtr = NULL;
ret = aclrtMalloc(&devPtr, MEMORY_SIZE, ACL_MEM_MALLOC_NORMAL_ONLY);
if (ret != ACL_SUCCESS) {
printf("Malloc device memory failed, error code = %d\n", ret);
aclrtResetDevice(0);
aclFinalize();
return -1;
}
printf("Malloc device memory success! Address: %p\n", devPtr);
// ... 核心业务逻辑(如数据拷贝、模型执行)...
printf("Now we can do AI computation here...\n");
// 3. 释放Device内存
ret = aclrtFree(devPtr);
if (ret != ACL_SUCCESS) {
printf("Free device memory failed, error code = %d\n", ret);
} else {
printf("Free device memory success!\n");
}
// 资源清理
ret = aclrtResetDevice(0); // 重置当前Device
if (ret != ACL_SUCCESS) {
printf("Reset Device failed, error code = %d\n", ret);
}
aclFinalize(); // 去初始化
printf("Program exit.\n");
return 0;
}
编译与运行指南
CMake配置详解
# 指定CMake最低版本要求
cmake_minimum_required(VERSION 3.10)
# 项目基本信息
project(HELLO_WORLD)
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
# 查找必需的依赖包
find_package(OpenMP REQUIRED)
# 设置头文件路径
include_directories(
/usr/local/Ascend/ascend-toolkit/latest/include/ # AscendCL头文件路径
)
# 设置链接库路径
link_directories(
/usr/local/Ascend/ascend-toolkit/latest/lib64/ # AscendCL库文件路径
)
# 添加可执行文件
add_executable(hello_world src/main.c)
# 链接必要的库
target_link_libraries(hello_world
ascendcl # AscendCL核心库
pthread # POSIX线程库
dl # 动态链接库
)
编译流程
运行程序
./hello_world
预期输出示例:
AscendCL init success!
Malloc device memory success! Address: 0xXXXXXX
Now we can do AI computation here...
Free device memory success!
Program exit.
开发最佳实践
错误处理规范
资源管理要点
常见问题
-
// main.c #include <stdio.h> #include “acl/acl.h” #define MEMORY_SIZE (100) int main() { // 初始化 const char *aclConfigPath = “”; aclError ret = aclInit(aclConfigPath); if (ret != ACL_SUCCESS) { printf(“AscendCL init failed, error code = %d\n”, ret); return -1; } printf(“AscendCL init success!\n”); // 1. 指定运算的Device ret = aclrtSetDevice(0); // 使用第0号Device if (ret != ACL_SUCCESS) { printf(“Set Device failed, error code = %d\n”, ret); aclFinalize(); return -1; } // 2. 申请Device内存 void* devPtr = NULL; ret = aclrtMalloc(&devPtr, MEMORY_SIZE, ACL_MEM_MALLOC_NORMAL_ONLY); if (ret != ACL_SUCCESS) { printf(“Malloc device memory failed, error code = %d\n”, ret); aclrtResetDevice(0); aclFinalize(); return -1; } printf(“Malloc device memory success! Address: %p\n”, devPtr); // ... 这里本应是我们的核心业务逻辑(如数据拷贝、模型执行)... printf(“Now we can do AI computation here...\n”); // 3. 释放Device内存 ret = aclrtFree(devPtr); if (ret != ACL_SUCCESS) { printf(“Free device memory failed, error code = %d\n”, ret); } else { printf(“Free device memory success!\n”); } // 资源清理 ret = aclrtResetDevice(0); // 重置当前Device if (ret != ACL_SUCCESS) { printf(“Reset Device failed, error code = %d\n”, ret); } aclFinalize(); // 去初始化 printf(“Program exit.\n”); return 0; }3. 编译与运行
3.1 编写CMakeLists.txt
# 指定CMake最低版本 cmake_minimum_required(VERSION 3.10) # 指定项目名称 project(HELLO_WORLD) # 设置C++标准 set(CMAKE_CXX_STANDARD 11) # 查找必需的依赖包 find_package(OpenMP REQUIRED) # 设置头文件路径 include_directories( /usr/local/Ascend/ascend-toolkit/latest/include/ ) # 设置链接库路径 link_directories( /usr/local/Ascend/ascend-toolkit/latest/lib64/ ) # 添加可执行文件 add_executable(hello_world main.c) # 链接库 target_link_libraries(hello_world ascendcl pthread dl )AscendCL 项目创建与开发指南
项目创建与准备
项目目录结构规划
在开始AscendCL编程之前,建议采用以下标准化的项目目录结构:
ascend_hello_world/ ├── CMakeLists.txt # 项目构建配置文件 ├── build/ # 编译输出目录 └── src/ # 源代码目录 └── main.c # 主程序文件这种结构清晰地区分了源代码、构建文件和配置文件,符合现代软件开发的最佳实践。
详细创建步骤
-
创建项目根目录:
mkdir -p ascend_hello_world/src ascend_hello_world/build cd ascend_hello_world这里使用
-p参数确保父目录不存在时自动创建,避免错误。 -
创建源代码文件:
touch src/main.c这将创建一个空的C源文件,我们将在其中编写AscendCL程序。
-
创建构建配置文件:
touch CMakeLists.txtCMake是跨平台的构建系统,可以简化项目配置和编译过程。
-
环境初始化 (aclInit):
- 参数
configPath:可指定配置文件路径,NULL表示使用默认配置 - 必须在所有AscendCL操作之前调用
- 典型应用场景:AI模型推理前的环境准备
- 参数
-
设备内存分配 (aclrtMalloc):
void **devPtr:双指针,用于返回分配的内存地址size_t size:分配的内存大小(单位字节)aclrtMemMallocPolicy policy:内存分配策略,常见选项:ACL_MEM_MALLOC_NORMAL_ONLY:普通内存ACL_MEM_MALLOC_HUGE_FIRST:优先分配大页内存
-
内存释放 (aclrtFree):
- 必须与
aclrtMalloc成对使用 - 典型错误:内存泄漏(忘记释放)或重复释放
- 必须与
-
环境清理 (aclFinalize):
- 释放所有AscendCL分配的资源
- 应在程序退出前调用
-
创建构建目录:
mkdir build && cd build -
生成构建系统:
cmake ..这会根据CMakeLists.txt生成Makefile等构建文件
-
编译项目:
make编译成功后会在build目录下生成可执行文件
hello_world -
检查所有API返回值:
aclError ret = aclrtMalloc(&devPtr, size, policy); if (ret != ACL_SUCCESS) { printf("Error %d in aclrtMalloc\n", ret); // 清理已分配的资源 return -1; } -
错误代码处理:
- 常见的错误代码:
ACL_ERROR_INVALID_PARAM:参数错误ACL_ERROR_MEMORY_ALLOCATION:内存分配失败ACL_ERROR_RT_FAILURE:运行时错误
- 常见的错误代码:
-
资源申请与释放:
- 遵循"谁申请,谁释放"原则
- 示例:
void* devPtr = NULL; aclrtMalloc(&devPtr, size); // 申请 /* 使用内存 */ aclrtFree(devPtr); // 释放
-
生命周期管理:
aclInit和aclFinalize是程序的生命周期边界- 典型模式:
aclInit(); // 程序开始 /* 主逻辑 */ aclFinalize(); // 程序结束
-
设备管理:
- 使用
aclrtSetDevice设置当前设备 - 退出前使用
aclrtResetDevice重置设备状态
- 使用
-
重复初始化:
// 错误示例 for(int i=0; i<10; i++) { aclInit(); // 这将导致错误 } -
内存泄漏:
void* ptr1, ptr2; aclrtMalloc(&ptr1, size); // 分配内存1 aclrtMalloc(&ptr2, size); // 分配内存2 aclrtFree(ptr1); // 只释放了内存1,内存2泄漏 -
错误处理不完整:
// 不推荐的写法 aclrtMalloc(&ptr, size); // 没有检查返回值 -
mkdir build cd build cmake .. make编译成功后,会在
build目录下生成可执行文件hello_world。
3.3 运行程序 在终端执行编译生成的可执行文件:
./hello_world
程序运行输出示例:
AscendCL init success! # 表示AscendCL初始化成功
Malloc device memory success! Address: 0xXXXXXX # 成功申请设备内存,显示分配地址
Now we can do AI computation here... # 表示可以在此处进行AI计算
Free device memory success! # 成功释放设备内存
Program exit. # 程序正常退出
- 代码解析与要点
错误处理:
- 每一个AscendCL API调用后都应使用类似以下代码检查返回值:
aclError ret = aclrtMalloc(...);
if (ret != ACL_SUCCESS) {
// 错误处理逻辑
printf("Failed to malloc device memory, error code: %d\n", ret);
return -1;
}
- 这是写出健壮程序的基础,可以及时发现API调用异常
资源管理:
- 遵循"谁申请,谁释放"的原则,确保资源不泄露
- aclrtMalloc与aclrtFree必须成对出现,例如:
void* devPtr = nullptr;
aclrtMalloc(&devPtr, size); // 申请
// ...使用内存...
aclrtFree(devPtr); // 释放
生命周期管理:
- aclInit和aclFinalize是程序的起点和终点
- 一个进程内只需调用一次,通常:
- aclInit在程序开始时调用
- aclFinalize在程序退出前调用
- 错误示例:在循环中重复调用aclInit会导致错误
结语:
恭喜你!你已经成功运行了第一个昇腾AI程序。虽然它还没有进行任何实际的AI计算,但它已经完整地走遍了AscendCL应用的核心生命周期。在下一篇中,我们将在此基础上,加载一个真实的模型,完成一次图像分类推理。
2025年昇腾CANN训练营第二季,基于CANN开源开放全场景,推出0基础入门系列、码力全开特辑、开发者案例等专题课程,助力不同阶段开发者快速提升算子开发技能。获得Ascend C算子中级认证,即可领取精美证书,完成社区任务更有机会赢取华为手机,平板、开发板等大奖。
报名链接:https://www.hiascend.com/developer/activities/cann20252
更多推荐




所有评论(0)