已合并
【PR】: 编程指南增加了context管理和兼容性处理章节 #2262
tingwood创建于 5月20日
【PR】: 编程指南增加了context管理和兼容性处理章节 #2262
已合并
tingwood创建于 5月20日
8 个文件变更+556-5
@@ -15,8 +15,10 @@
15 - ### [4.2 跨流捕获](04-02_跨流捕获.md)15 - ### [4.2 跨流捕获](04-02_跨流捕获.md)
16 - ### [4.3 任务更新](04-03_任务更新.md)16 - ### [4.3 任务更新](04-03_任务更新.md)
17- ## [5. 多设备编程](05_多设备编程.md)17- ## [5. 多设备编程](05_多设备编程.md)
18-- ## [6. 进程间通信](06_进程间通信.md)18+- ## [6. Context管理](06_Context管理.md)
19-- ## [7. 运行时核资源控制](07_运行时核资源控制.md)19+- ## [7. 进程间通信](07_进程间通信.md)
20-- ## [8. 配置AI Core栈空间大小](08_配置AI-Core栈空间大小.md)20+- ## [8. 运行时核资源控制](08_运行时核资源控制.md)
21-- ## [9. 异常处理](09_异常处理.md)21+- ## [9. 配置AI Core栈空间大小](09_配置AI-Core栈空间大小.md)
22+- ## [10. 异常处理](10_异常处理.md)
23+- ## [11. 兼容性处理](11_兼容性处理.md)
22 24 
@@ -97,7 +97,7 @@ myKernel<<<8, nullptr, s0>>>(); // 在Device 1上通过Stream s0下发算子
97 97 
98## 跨Device的数据交互98## 跨Device的数据交互
99 99 
100-本节中的“跨Device的数据交互”是指一个进程内、根据硬件组网(例如处于PCIe或者HCCS互联的组网拓扑下)、Device之间能够访问彼此的内存。可以使用aclrtDeviceCanAccessPeer接口查询两个Device之间是否支持数据交互,若支持,再根据访问方向,分别调用aclrtDeviceEnablePeerAccess接口开启一个Device到另一个Device的数据交互功能,例如,调用一次aclrtDeviceEnablePeerAccess接口开启Device 0到Device 1的数据交互,再调用一次aclrtDeviceEnablePeerAccess接口开启Device 1到Device 0的数据交互。若需关闭Device之间的数据交互,可调用aclrtDeviceDisablePeerAccess接口。对于两个进程之间的通信请参见[进程间通信](06_进程间通信.md)。100+本节中的“跨Device的数据交互”是指一个进程内、根据硬件组网(例如处于PCIe或者HCCS互联的组网拓扑下)、Device之间能够访问彼此的内存。可以使用aclrtDeviceCanAccessPeer接口查询两个Device之间是否支持数据交互,若支持,再根据访问方向,分别调用aclrtDeviceEnablePeerAccess接口开启一个Device到另一个Device的数据交互功能,例如,调用一次aclrtDeviceEnablePeerAccess接口开启Device 0到Device 1的数据交互,再调用一次aclrtDeviceEnablePeerAccess接口开启Device 1到Device 0的数据交互。若需关闭Device之间的数据交互,可调用aclrtDeviceDisablePeerAccess接口。对于两个进程之间的通信请参见[进程间通信](07_进程间通信.md)。
101 101 
102以下是跨Device内存复制的代码示例,不可以直接拷贝编译运行,仅供参考。完整样例代码请参见[Link](https://gitcode.com/cann/runtime/tree/master/example/1_basic_features/device/2_device_P2P)。102以下是跨Device内存复制的代码示例,不可以直接拷贝编译运行,仅供参考。完整样例代码请参见[Link](https://gitcode.com/cann/runtime/tree/master/example/1_basic_features/device/2_device_P2P)。
103 103 
@@ -0,0 +1,316 @@
1+# Context管理
2+ 
3+## Context概念
4+ 
5+Context是CANN Runtime中的核心抽象,代表一个Device上的执行上下文环境。它封装了Device上的计算资源、内存资源、Stream资源等运行时状态,是Runtime操作的基础载体。
6+ 
7+每个Context与特定的Device绑定,包含该Device上的:
8+ 
9+- 计算资源:用于执行Kernel、系统任务等
10+- 内存资源:设备内存分配与管理
11+- Stream资源:任务队列及调度状态
12+- 运行时配置:影响任务执行的参数
13+ 
14+Context与线程绑定,同一时刻一个线程只能使用一个Context。Runtime接口在执行时,会自动使用当前线程绑定的Context。
15+ 
16+<br>
17+<br>
18+ 
19+## 为什么要用Context
20+ 
21+### 使用Context的核心场景
22+ 
23+1. **多线程并行计算**:多个线程并发使用同一Device时,各自创建独立Context,避免资源竞争和状态混乱。
24+2. **精细资源控制**:需要更细粒度地控制资源创建、使用、释放时机。
25+3. **多线程Context共享**:在同进程多线程场景下,显式创建的Context可在多线程间共享使用。
26+4. **模块化程序设计**:不同功能模块使用独立Context,便于资源管理和问题定位。
27+ 
28+### 显式Context vs 默认Context
29+ 
30+调用`aclrtSetDevice`接口时,Runtime会自动为指定Device创建一个**默认Context**。对于简单应用,使用默认Context即可满足需求。但对于复杂应用,显式创建和管理Context具有以下优势:
31+ 
32+ 
33+| 场景 | 默认Context | 显式Context |
34+| ------------ | ----------------------------------------------- | --------------------------------------- |
35+| 多线程编程 | 线程间共享默认Context,任务执行顺序依赖线程调度 | 每个线程独立Context,便于隔离和调试 |
36+| 资源隔离 | 同一Device上的不同模块共享资源 | 不同模块使用独立Context,资源隔离更清晰 |
37+| 代码可维护性 | 线程切换Device时Context状态不明确 | 显式指定Context,代码意图清晰 |
38+| 资源生命周期 | 随Device生命周期管理 | 独立控制Context创建和销毁时机 |
39+ 
40+<br>
41+ 
42+## Context与Device、Stream的关系
43+ 
44+Context与Device、Stream的关系如下图所示:
45+ 
46+```
47+┌─────────────────────────────────────────────────────────────┐
48+│ Host进程 │
49+│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
50+│ │ Thread 1 │ │ Thread 2 │ │ Thread 3 │ │
51+│ │ Context A │ │ Context B │ │ Context A │ │
52+│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
53+│ │ │ │ │
54+└─────────┼─────────────────┼─────────────────┼───────────────┘
55+ │ │ │
56+ ▼ ▼ ▼
57+┌────────────────────────────────────────────────────────────┐
58+│ Device 0 (NPU) │
59+│ ┌─────────────────────────────────────────────────────┐ │
60+│ │ Context A │ │
61+│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
62+│ │ │ Stream 0 │ │ Stream 1 │ │ Stream 2 │ ... │ │
63+│ │ │ (default)│ │ │ │ │ │ │
64+│ │ └──────────┘ └──────────┘ └──────────┘ │ │
65+│ └─────────────────────────────────────────────────────┘ │
66+│ ┌─────────────────────────────────────────────────────┐ │
67+│ │ Context B │ │
68+│ │ ┌──────────┐ ┌──────────┐ │ │
69+│ │ │ Stream 0 │ │ Stream 1 │ ... │ │
70+│ │ │ (default)│ │ │ │ │
71+│ │ └──────────┘ └──────────┘ │ │
72+│ └─────────────────────────────────────────────────────┘ │
73+└────────────────────────────────────────────────────────────┘
74+```
75+ 
76+- **Device**:物理NPU设备,一个Device上可创建多个Context。
77+- **Context**:Device上的执行上下文,包含默认Stream和用户创建的Stream。
78+- **Stream**:任务队列,归属于特定Context。
79+ <br>
80+ 
81+## 如何使用Context
82+ 
83+### 单线程基础使用模式(默认Context)
84+ 
85+对于简单应用,使用默认Context即可满足需求。调用`aclrtSetDevice`接口时,Runtime会自动创建默认Context,无需显式管理。
86+ 
87+以下示例展示单线程使用默认Context的基础流程:
88+ 
89+```c
90+// 1. 初始化Runtime
91+aclInit(nullptr);
92+ 
93+// 2. 指定Device,自动创建默认Context和默认Stream
94+int32_t deviceId = 0;
95+aclrtSetDevice(deviceId);
96+ 
97+// 3. 创建显式Stream(可选,也可直接使用默认Stream传入nullptr)
98+aclrtStream stream;
99+aclrtCreateStream(&stream);
100+ 
101+// 4. 在Stream上下发任务
102+aclrtMemcpyAsync(devPtr, size, hostPtr, size, ACL_MEMCPY_HOST_TO_DEVICE, stream);
103+myKernel<<<8, nullptr, stream>>>(devPtr, size);
104+aclrtMemcpyAsync(hostPtr, size, devPtr, size, ACL_MEMCPY_DEVICE_TO_HOST, stream);
105+ 
106+// 5. 同步等待任务完成
107+aclrtSynchronizeStream(stream);
108+ 
109+// 6. 销毁显式Stream
110+aclrtDestroyStream(stream);
111+ 
112+// 7. 复位Device,释放默认Context和默认Stream
113+aclrtResetDeviceForce(deviceId);
114+ 
115+// 8. 去初始化
116+aclFinalize();
117+```
118+ 
119+<br>
120+ 
121+### 多线程并行计算
122+ 
123+多个线程使用同一Device时,推荐为每个线程创建独立Context:
124+ 
125+```c
126+// 线程函数
127+void* threadFunc(void* arg) {
128+ int32_t deviceId = *(int32_t*)arg;
129+
130+ // 每个线程创建自己的Context
131+ aclrtContext ctx;
132+ aclrtCreateContext(&ctx, deviceId);
133+
134+ // 创建Stream
135+ aclrtStream stream;
136+ aclrtCreateStream(&stream);
137+
138+ // 执行任务
139+ // ... 业务逻辑 ...
140+
141+ // 同步等待任务完成
142+ aclrtSynchronizeStream(stream);
143+
144+ // 销毁资源
145+ aclrtDestroyStream(stream);
146+ aclrtDestroyContext(ctx);
147+
148+ return nullptr;
149+}
150+ 
151+// 主线程
152+int main() {
153+ aclInit(nullptr);
154+
155+ int32_t deviceId = 0;
156+ aclrtSetDevice(deviceId);
157+
158+ // 创建多个线程
159+ pthread_t threads[4];
160+ for (int i = 0; i < 4; i++) {
161+ pthread_create(&threads[i], nullptr, threadFunc, &deviceId);
162+ }
163+
164+ // 等待线程完成
165+ for (int i = 0; i < 4; i++) {
166+ pthread_join(threads[i], nullptr);
167+ }
168+
169+ aclrtResetDeviceForce(deviceId);
170+ aclFinalize();
171+ return 0;
172+}
173+```
174+ 
175+<br>
176+ 
177+### 多线程Context共享
178+ 
179+多个线程共享同一Context时,需自行保证Stream上任务的执行顺序:
180+ 
181+```c
182+aclrtContext g_ctx; // 全局Context
183+ 
184+void* threadFunc(void* arg) {
185+ int threadId = *(int*)arg;
186+
187+ // 切换到共享Context
188+ aclrtSetCurrentContext(g_ctx);
189+
190+ // 创建线程专属Stream
191+ aclrtStream stream;
192+ aclrtCreateStream(&stream);
193+
194+ // 在自己的Stream上执行任务
195+ // ... 业务逻辑 ...
196+
197+ aclrtSynchronizeStream(stream);
198+ aclrtDestroyStream(stream);
199+
200+ return nullptr;
201+}
202+ 
203+int main() {
204+ aclInit(nullptr);
205+
206+ int32_t deviceId = 0;
207+ aclrtSetDevice(deviceId);
208+
209+ // 创建一个Context供多线程共享
210+ aclrtCreateContext(&g_ctx, deviceId);
211+
212+ pthread_t threads[4];
213+ int threadIds[4];
214+ for (int i = 0; i < 4; i++) {
215+ threadIds[i] = i;
216+ pthread_create(&threads[i], nullptr, threadFunc, &threadIds[i]);
217+ }
218+
219+ for (int i = 0; i < 4; i++) {
220+ pthread_join(threads[i], nullptr);
221+ }
222+
223+ aclrtDestroyContext(g_ctx);
224+ aclrtResetDeviceForce(deviceId);
225+ aclFinalize();
226+ return 0;
227+}
228+```
229+ 
230+<br>
231+ 
232+### Context切换
233+ 
234+使用`aclrtSetCurrentContext`切换当前线程的Context:
235+ 
236+```c
237+// 在Device 0上创建两个Context
238+aclrtSetDevice(0);
239+aclrtContext ctx1, ctx2;
240+aclrtCreateContext(&ctx1, 0);
241+aclrtCreateContext(&ctx2, 0);
242+ 
243+// 切换到ctx1
244+aclrtSetCurrentContext(ctx1);
245+aclrtStream stream1;
246+aclrtCreateStream(&stream1);
247+// ... 在ctx1上执行任务 ...
248+ 
249+// 切换到ctx2
250+aclrtSetCurrentContext(ctx2);
251+aclrtStream stream2;
252+aclrtCreateStream(&stream2);
253+// ... 在ctx2上执行任务 ...
254+ 
255+// 切换Context时,如果新Context属于不同Device,Device也会随之切换
256+aclrtSetDevice(1);
257+aclrtContext ctx3;
258+aclrtCreateContext(&ctx3, 1);
259+ 
260+aclrtSetCurrentContext(ctx3); // 当前Context切换为ctx3,Device也切换为1
261+```
262+ 
263+<br>
264+ 
265+### Context参数配置
266+ 
267+使用`aclrtCtxSetSysParamOpt`设置Context级别的参数:
268+ 
269+```c
270+aclrtContext ctx;
271+aclrtCreateContext(&ctx, 0);
272+ 
273+// 设置Context参数(示例:设置内存配置)
274+aclrtCtxSetSysParamOpt(ACL_SYS_PARAM_OPT_XXX, value);
275+ 
276+// 获取Context参数
277+int64_t paramValue;
278+aclrtCtxGetSysParamOpt(ACL_SYS_PARAM_OPT_XXX, &paramValue);
279+```
280+ 
281+<br>
282+ 
283+### 默认Stream获取
284+ 
285+每个Context包含一个默认Stream,可通过接口获取:
286+ 
287+```c
288+aclrtContext ctx;
289+aclrtCreateContext(&ctx, 0);
290+ 
291+// 获取当前Context的默认Stream
292+aclrtStream defaultStream;
293+aclrtCtxGetCurrentDefaultStream(&defaultStream);
294+ 
295+// 使用默认Stream(也可直接传nullptr)
296+aclrtMemcpyAsync(dst, size, src, size, ACL_MEMCPY_DEVICE_TO_DEVICE, nullptr);
297+```
298+ 
299+<br>
300+ 
301+## 如何用好Context
302+ 
303+### 推荐做法
304+ 
305+1. **优先使用显式Context**:对于多线程或复杂应用,显式创建Context,代码意图更清晰。
306+2. **Context与线程绑定**:推荐在创建Context的线程中使用该Context,避免跨线程使用带来的复杂性。
307+3. **及时销毁资源**:Context不再使用时及时销毁,避免资源泄漏。
308+4. **使用aclrtResetDeviceForce**:Device使用完毕后,使用`aclrtResetDeviceForce`一次性释放Device上所有资源。
309+5. **明确Context切换时机**:在多Context场景下,显式调用`aclrtSetCurrentContext`切换,增加代码可读性。
310+ 
311+### 避免的做法
312+ 
313+1. **不要销毁默认Context**:默认Context由Runtime管理,不能调用`aclrtDestroyContext`销毁。
314+2. **避免Context状态不确定**:多线程共享Context时,避免依赖Context的隐式状态切换。
315+3. **不要跨线程随意操作同一Stream**:如果必须在多线程间共享Context,每个线程应使用独立的Stream。
316+4. **不要在已复位的Device上使用Context**:Device被复位后,相关Context不可用。
Rdocs/02_dev_guide/06_进程间通信.mddocs/02_dev_guide/07_进程间通信.md+0-0
文件重命名但无更改。
Rdocs/02_dev_guide/07_运行时核资源控制.mddocs/02_dev_guide/08_运行时核资源控制.md+0-0
文件重命名但无更改。
Rdocs/02_dev_guide/08_配置AI-Core栈空间大小.mddocs/02_dev_guide/09_配置AI-Core栈空间大小.md+0-0
文件重命名但无更改。
Rdocs/02_dev_guide/09_异常处理.mddocs/02_dev_guide/10_异常处理.md+0-0
文件重命名但无更改。
@@ -0,0 +1,233 @@
1+# 兼容性处理
2+ 
3+CANN Runtime 遵循语义化版本规范,提供版本查询机制和废弃接口处理策略,帮助开发者构建前后向兼容的应用程序。
4+ 
5+<br>
6+<br>
7+ 
8+## 1. 版本查询机制
9+ 
10+Runtime提供多层次的版本查询接口,帮助应用在运行时获取环境信息,实现兼容性适配。
11+ 
12+### 1.1 Runtime版本查询
13+ 
14+使用`aclsysGetVersionNum`接口查询Runtime版本号,传入包名`"runtime"`
15+ 
16+```c
17+char pkgName[] = "runtime";
18+int32_t versionNum;
19+aclsysGetVersionNum(pkgName, &versionNum);
20+ 
21+printf("Runtime Version Num: %d\n", versionNum);
22+```
23+ 
24+**版本号计算规则**
25+ 
26+返回的`versionNum`是一个整数值,遵循语义化版本编码规则,便于版本比较。
27+ 
28+| 版本部分 | 权重 | 说明 |
29+| ----------------- | -------- | ------------------ |
30+| major(主版本号) | 10000000 | 不兼容的API变更 |
31+| minor(次版本号) | 100000 | 向后兼容的功能新增 |
32+| patch(修订号) | 1000 | 向后兼容的问题修复 |
33+ 
34+**正式版本计算公式:**
35+ 
36+```
37+versionNum = major × 10000000 + minor × 100000 + patch × 1000
38+```
39+ 
40+**计算示例:**
41+ 
42+| 版本字符串 | 计算过程 | versionNum |
43+| ---------- | --------------------------------- | ---------- |
44+| 9.0.0 | 9×10000000 + 0×100000 + 0×1000 | 90000000 |
45+| 8.5.1 | 8×10000000 + 5×100000 + 1×1000 | 80501000 |
46+ 
47+<br>
48+ 
49+### 1.2 Driver版本查询
50+ 
51+Runtime依赖驱动能力,部分特性功能需要同时判断驱动版本号以做兼容性处理,传入"driver"包名可查询驱动版本号。
52+ 
53+```c
54+char pkgName[] = "driver"; // 查询驱动版本,使用"driver"
55+int32_t versionNum;
56+aclsysGetVersionNum(pkgName, &versionNum);
57+printf("Package Version Num: %d\n", versionNum);
58+```
59+ 
60+<br>
61+ 
62+### 1.3 运行时特性查询
63+ 
64+使用`aclrtGetDevFeature`查询设备支持的特性能力,实现功能特性的条件适配:
65+ 
66+```c
67+int32_t deviceId = 0;
68+aclrtSetDevice(deviceId);
69+ 
70+// 查询是否支持某特性
71+int32_t isSupported = 0;
72+aclrtGetDevFeature(deviceId, ACL_FEATURE_XXX, &isSupported);
73+ 
74+if (isSupported) {
75+ // 使用新特性
76+} else {
77+ // 使用兼容方案
78+}
79+```
80+ 
81+<br>
82+<br>
83+ 
84+## 2. 应用兼容性处理
85+ 
86+应用开发时预留扩展能力便于前向兼容(适配新版本)或者新版本应用需在旧版本环境运行时做后向兼容(支持旧版本),可采用如下处理方式:
87+ 
88+* **运行时版本检测**:程序启动时获取版本信息,记录日志或进行适配判断。
89+ 
90+```c
91+// 版本条件执行
92+int32_t versionNum;
93+aclsysGetVersionNum("runtime", &versionNum);
94+ 
95+int32_t major = 8;
96+int32_t minor = 5;
97+ 
98+if (versionNum >= major * 10000000 + minor * 100000) {
99+ // 使用新版本特性接口
100+ aclrtQueryEventStatus(event, &status);
101+} else {
102+ // 使用旧版本兼容接口
103+ aclrtQueryEvent(event, &status);
104+}
105+```
106+ 
107+* **特性能力探测**:使用特性查询接口而非硬编码版本号判断。
108+ 
109+```c
110+// 查询特性支持情况
111+int32_t isSupported = 0;
112+aclrtGetDevFeature(deviceId, ACL_FEATURE_TSCPU_TASK_UPDATE_SUPPORT_AIC_AIV, &isSupported);
113+ 
114+if (isSupported) {
115+ // 使用新特性实现高效路径
116+ useNewFeaturePath();
117+} else {
118+ // 降级到兼容实现
119+ useLegacyPath();
120+}
121+```
122+<br>
123+<br>
124+ 
125+## 3. CANN Runtime兼容性策略
126+ 
127+### 3.1 兼容原则
128+ 
129+CANN Runtime遵循以下兼容性原则:
130+ 
131+| 版本变更类型 | 兼容性保证 | 示例 |
132+| ------------- | ------------ | ---------------------- |
133+| Patch版本升级 | 完全向后兼容 | Bug修复、性能优化 |
134+| Minor版本升级 | API向后兼容 | 新增接口、新增特性 |
135+| Major版本升级 | 不保证兼容 | 删除废弃接口、架构调整 |
136+ 
137+### 3.2 废弃接口处理策略
138+ 
139+Runtime使用`ACL_DEPRECATED_MESSAGE`宏标记废弃接口,在废弃周期内保持可用,同时提示替换接口(如有)。
140+ 
141+#### 废弃接口标记方式
142+ 
143+```c
144+// 废弃接口声明示例
145+ACL_DEPRECATED_MESSAGE("aclrtQueryEvent is deprecated, use aclrtQueryEventStatus instead")
146+ACL_FUNC_VISIBILITY aclError aclrtQueryEvent(aclrtEvent event, aclrtEventStatus *status);
147+```
148+ 
149+编译时将产生警告信息:
150+ 
151+```
152+warning: 'aclrtQueryEvent' is deprecated: aclrtQueryEvent is deprecated, use aclrtQueryEventStatus instead [-Wdeprecated-declarations]
153+```
154+ 
155+#### 废弃接口生命周期
156+ 
157+| 阶段 | 状态 | 建议 |
158+| ------------ | ------------------ | ------------------ |
159+| 发布废弃通知 | 接口可用,编译警告 | 开始迁移到替代接口 |
160+| 废弃过渡期 | 接口可用,持续警告 | 完成迁移 |
161+| 正式移除 | 接口不可用 | 必须使用替代接口 |
162+ 
163+### 3.3 枚举/结构体成员废弃处理
164+ 
165+枚举值和结构体成员也可能被废弃:
166+ 
167+```c
168+typedef enum aclrtLaunchKernelAttrId {
169+ ACL_RT_LAUNCH_KERNEL_ATTR_LOCAL_MEMORY_SIZE
170+ ACL_DEPRECATED_MESSAGE("Use ACL_RT_LAUNCH_KERNEL_ATTR_DYN_UBUF_SIZE instead") = 2,
171+ ACL_RT_LAUNCH_KERNEL_ATTR_DYN_UBUF_SIZE = 2, // 替代值,同编号
172+ ...
173+} aclrtLaunchKernelAttrId;
174+ 
175+typedef union aclrtLaunchKernelAttrValue {
176+ ACL_DEPRECATED_MESSAGE("Use dynUbufSize instead")
177+ uint32_t localMemorySize; // 废弃成员
178+ uint32_t dynUBufSize; // 替代成员
179+ ...
180+} aclrtLaunchKernelAttrValue;
181+```
182+ 
183+处理建议:
184+ 
185+- 使用替代枚举值/成员名。
186+- 注意替代值可能与废弃值编号相同,保持兼容。
187+ 
188+<br>
189+<br>
190+ 
191+## 4. 迁移废弃接口
192+ 
193+### 4.1 迁移流程
194+ 
195+1. **识别废弃接口**:编译时查看废弃警告,确认替代接口。
196+2. **评估迁移影响**:分析接口参数、返回值、语义差异。
197+3. **编写迁移代码**:替换废弃接口调用,适配新接口参数。
198+4. **测试验证**:确保迁移后功能正确、性能符合预期。
199+ 
200+### 4.2 迁移示例
201+ 
202+#### aclrtQueryEvent -> aclrtQueryEventStatus
203+ 
204+```c
205+// 废弃接口(旧代码)
206+aclrtEventStatus status;
207+aclrtQueryEvent(event, &status);
208+if (status == ACL_EVENT_STATUS_COMPLETE) {
209+ // ...
210+}
211+ 
212+// 替代接口(新代码)
213+aclrtEventRecordedStatus status;
214+aclrtQueryEventStatus(event, &status);
215+if (status == ACL_EVENT_RECORDED_STATUS_COMPLETE) {
216+ // ...
217+}
218+```
219+ 
220+### 4.3 推荐做法
221+ 
222+1. **定期检查编译警告**:及时处理废弃接口警告,避免积累。
223+2. **优先使用新接口**:新接口通常有更好的语义、性能或功能。
224+3. **版本信息记录**:应用启动时记录Runtime版本,便于问题定位。
225+4. **特性探测优于版本判断**:使用特性查询接口判断能力,而非硬编码版本号。
226+5. **封装版本适配逻辑**:将兼容性逻辑集中在适配层,便于维护。
227+ 
228+### 4.4 避免的做法
229+ 
230+1. **不要忽略废弃警告**:废弃接口可能在未来版本移除,导致编译失败。
231+2. **不要硬编码版本判断**:版本号判断缺乏灵活性,应使用特性探测。
232+3. **不要混用新旧接口**:同一功能模块统一使用新或旧接口,避免混乱。
233+4. **不要假设接口语义不变**:迁移时仔细阅读新接口文档,确认语义一致。