AI应用进程未退出,导致休眠唤醒失败
VDEC视频解码无报错,但无解码结果数据、CPU占用率高
现象描述
查看应用类日志,无ERROR报错、无解码结果数据输出,另外,在运行应用程序的Linux服务器上执行top命令,该应用进程的cpu占用率持续升高。
可能原因
-
无ERROR、无解码结果数据输出,判断可能是因为解码发帧接口aclvdecSendFrame调用正常,但未触发回调函数,无法获取解码结果数据。
-
检查触发回调函数的代码逻辑。
按照VDEC视频解码的接口调用逻辑:由用户提前创建一个单独的线程,并自定义线程函数,在线程函数内调用aclrtProcessReport接口,通过该接口配置超时时间,等待指定的超时时间后,触发回调函数,获取解码结果数据。
void *ThreadFunc(aclrtContext sharedContext){if (sharedContext == nullptr) {ERROR_LOG("sharedContext can not be nullptr");return ((void*)(-1));}INFO_LOG("use shared context for this thread");aclError ret = aclrtSetCurrentContext(sharedContext);if (ret != ACL_SUCCESS) {ERROR_LOG("aclrtSetCurrentContext failed, errorCode = %d", static_cast<int32_t>(ret));return ((void*)(-1));}INFO_LOG("thread start ");while (runFlag) {// Notice: timeout 1000ms(void)aclrtProcessReport(1000);}return (void*)0;} -
如果触发回调函数的接口调用逻辑正确,则在aclrtProcessReport接口处增加日志打印,判断应用运行过程中线程是否成功调用了aclrtProcessReport接口,只有成功调用aclrtProcessReport接口,才会触发回调函数。
示例代码如下:
while (runFlag) {// Notice: timeout 1000msaclError ret = aclrtProcessReport(1000);printf("aclrtProcessReport failed, ret=%d.\n", ret);} -
修改代码后,重新编译运行应用。
在终端屏幕重复出现以下打印信息,表示调用aclrtProcessReport接口失败:
aclrtProcessReport failed, ret = 107012查阅该接口的返回值说明,107012表示线程未订阅或重复订阅。
-
检查代码逻辑,检查是否调用aclvdecSetChannelDescThreadId接口绑定用户新建的线程,按照VDEC视频解码的接口调用逻辑,只有调用该接口绑定用户线程,才可以触发调用aclrtProcessReport接口,进而触发回调函数。
-
修改代码后,重新编译运行应用,VDEC视频解码正常,正常输出解码结果数据,同时cpu占用率下降。
解决办法
参见VDEC的VDEC视频解码或者参考Link上的VDEC功能样例开发VDEC功能。
其中,需关注以下注意点:
-
创建新线程,并自定义线程函数,在线程函数内调用aclrtProcessReport接口,等待指定时间后,触发回调函数中的回调函数。
-
需调用aclvdecSetChannelDescThreadId接口绑定用户创建的新线程。
-
释放资源时,依次销毁通道、销毁通道描述信息后,才可以销毁中用户创建的新线程。
父主题: FAQ
执行应用程序的权限不足导致AscendCL初始化报错
问题现象
用户进程报错并退出。
查看应用类日志,提示获取Device信息失败,最终导致AscendCL初始化失败,日志片段示例如下:
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.635 [runtime.cc:1065]89696 CheckHaveDevice:[INIT][DEFAULT]Call halGetDeviceInfo failed: drvRet=4, module type=0, info type=1.
[ERROR] ASCENDCL(89696,main):2023-03-07-17:13:27.994.723 [acl.cpp:164]89696 aclInit: [INIT][DEFAULT][Init][Version]init soc version failed, ret = 507008
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.774 [api_impl.cc:3490]89696 GetDevErrMsg:report error module_type=3, module_name=EE8888
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.798 [api_impl.cc:3490]89696 GetDevErrMsg:ctx is NULL!
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.827 [api_impl.cc:3546]89696 GetDevMsg:Failed to GetDeviceErrMsg, retCode=0x7070001.
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.849 [logger.cc:1348]89696 GetDevMsg:GetDeviceMsg failed, getMsgType=0.
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.888 [api_c.cc:3595]89696 rtGetDevMsg:ErrCode=107002, desc=[context pointer null], InnerCode=0x7070001
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.910 [error_message_manage.cc:49]89696 FuncErrorReason:report error module_type=3, module_name=EE8888
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.932 [error_message_manage.cc:49]89696 FuncErrorReason:rtGetDevMsg execute failed, reason=[context pointer null]
EL0003: The argument is invalid.
Solution: Try again with a valid argument.
TraceBack (most recent call last):
[Init][Version]init soc version failed, ret = 507008[FUNC:ReportInnerError][FILE:log_inner.cpp][LINE:145]
ctx is NULL![FUNC:GetDevErrMsg][FILE:api_impl.cc][LINE:3490]
rtGetDevMsg execute failed, reason=[context pointer null][FUNC:FuncErrorReason][FILE:error_message_manage.cc][LINE:49]
[ERROR] acl init failed
[ERROR] Sample init resource failed
原因分析
可能存在以下原因:
- Device状态异常,未正常启动。
- 执行应用程序的用户权限不足,无法查询Device信息。
解决办法
-
首先,确认Device是否正常启动。
-
以root用户登录安装Driver包的环境,执行以下命令查询其安装路径。
cat /etc/ascend_install.info在该文件中,Driver_Install_Path_Param表示Driver包的安装路径。
-
进入Driver安装路径,使用upgrade-tool工具查看下Device侧运行文件系统版本,如果能正常查询,则说明Device侧已经正常启动。
./upgrade-tool --device_index -1 --system_version正常查询返回信息类似如下:

-
-
其次,检查运行应用程序的用户权限是否正确。
要求运行应用程序的用户,需与Driver运行用户在一个属组内。在“cat /etc/passwd”文件中,可查看用户属组,Driver的默认运行用户为HwHiAiUser。
修改用户属组的命令示例如下:
usermod -g 组名 用户名 -
如果以上方法解决不了问题,则需要参考如下步骤将获取日志,反馈给技术支持。您可以获取日志后单击Link联系技术支持。
-
登录到运行应用程序的环境,执行如下命令将日志级别设置为Debug。
export ASCEND_GLOBAL_LOG_LEVEL=0 -
重新运行应用程序。
-
从日志存放路径下获取应用类日志。
存放日志的默认路径为“$HOME/ascend/log”。
-
使用msnpureport工具,获取指定Device上的Debug日志。
命令示例如下,其中deviceID需要设置为指定Device的ID:
msnpureport -g debug -d deviceID
-
父主题: FAQ
AI应用进程未退出,导致休眠唤醒失败
问题现象
休眠失败。
查看应用类日志,系统内部的任务分发模块hwts正处于busy状态,检查发现不满足休眠条件,日志片段示例如下:
[ERROR] TSCH(-1,null):2023-01-01-02:53:45.850.781 1 (dieid:0,cpuid:0) device_management_plat.c:563 suspend_ack: suspend pre check fail, hwts is busy
[EVENT] TSCH(-1,null):2023-01-01-02:53:45.850.803 2 (dieid:0,cpuid:0) device_management.c:411 process_low_power_cmd: ts suspend ack ret=1.
原因分析
根据休眠唤醒的流程,休眠前AI应用进程必须先退出,相关硬件资源处于idle态,才允许休眠。不满足休眠条件,会有相关报错,本案例中因为AI应用进程未退出,在休眠唤醒时检测到hwts处于busy状态,因此休眠失败。
解决办法
用户需要确保AI应用进程已经运行结束或者优雅退出,推荐使用kill -2 PID退出相关进程,PID需按照替换为实际进程ID。
父主题: FAQ
复用输出图片描述类型,VDEC视频解码报错,提示有不支持的图片格式
问题现象
循环调用aclvdecSendFrame接口解码视频中的每一帧码流时,在遇到异常帧之后,解码下一帧就会报错,退出应用进程。
分别查看Host侧日志、Device侧日志,发现Device日志中提示the out format 0 is not supported,日志片段如下:
- Device侧日志:
[ERROR] KERNEL(2234,sklogd):2023-06-13-19:21:22.987.969 [klogd.c:246][652145.056916] [HiDvpp][A618] [Vdec]:vdec_check_resize_param [Line]:6768 pid 23973 usr chn 0 device 0 chn 0 the out format 0 is not supported.
- Host侧日志:
[ERROR] RUNTIME(17174,AIMCDemo):2023-06-13-19:21:23.664.211 [api_c.cc:721]17184 rtStreamSynchronize:[DVPP][DEFAULT]ErrCode=507018, desc=[aicpu exception], InnerCode=0x715002a
[ERROR] RUNTIME(17174,AIMCDemo):2023-06-13-19:21:23.664.215 [error_message_manage.cc:49]17184 FuncErrorReason:[DVPP][DEFAULT]report error module_type=3, module_name=EE8888
[ERROR] RUNTIME(17174,AIMCDemo):2023-06-13-19:21:23.664.221 [error_message_manage.cc:49]17184 FuncErrorReason:[DVPP][DEFAULT]rtStreamSynchronize execute failed, reason=[aicpu exception]
[INFO] GE(17174,AIMCDemo):2023-06-13-19:21:23.664.227 [error_manager.cc:252]17184 ReportInterErrMessage:report error_message, error_code:EE8888, work_stream_id:1717417184
[ERROR] ASCENDCL(17174,AIMCDemo):2023-06-13-19:21:23.664.234 [video_processor_v200.cpp:1089]17184 aclvdecSendFrame: [DVPP][DEFAULT][Sync][Stream]vdec fail to synchronize sendFrameStream, runtime errorCode = 507018, channelId = 0.
原因分析
检查应用代码,发现循环解码视频中的每一帧码流时,复用aclvdecSendFrame接口的输出图片描述类型acldvppPicDesc,但在下一次解码前没有重新设置输出图片format、width、height、widthStride、heightStride,这时,如果前一帧解码失败,acldvppPicDesc的参数format、width、height、widthStride、heightStride变成默认值0,width、height、widthStride、heightStride为0时,vdec会以实际图片宽高解码输出,但format为0,表示YUV400格式,vdec不支持解码输出该格式,会导致下一帧参数不合法解码失败。
解决方法
优化应用代码逻辑,复用输出图片描述类型acldvppPicDesc时,在下一次解码前需重新设置输出图片format、width、height、widthStride、heightStride。
正例代码片段:
aclError ret;
int restLen = 10;
uint32_t inBufferSize = 0;
void *g_picOutBufferDev;
void *inBufferDev = nullptr;
acldvppPicDesc *picOutputDesc;
size_t dataSize = (INPUT_WIDTH * INPUT_HEIGHT * 3) / 2;
// 申请一个picOutputDesc,每帧复用
picOutputDesc = acldvppCreatePicDesc();
// read file to device memory
ReadFileToDeviceMem(filePath.c_str(), inBufferDev, inBufferSize);
while (restLen > 0) {
// 等待前一个使用picOutputDesc解码帧结束,重新复用picOutputDesc,并针对这一帧重新设置Format、width、height、widthStride、heightStride参数值
ret = acldvppSetPicDescFormat(picOutputDesc, static_cast<acldvppPixelFormat>(1)); // 1:YUV420 semi-planner(nv12)
ret = acldvppSetPicDescWidth(picOutputDesc, 1920);
ret = acldvppSetPicDescHeight(picOutputDesc, 1080);
ret = acldvppSetPicDescWidthStride(picOutputDesc, 1920);
ret = acldvppSetPicDescHeightStride(picOutputDesc, 1080);
ret = acldvppMalloc(&g_picOutBufferDev, dataSize);
ret = acldvppSetPicDescData(picOutputDesc, g_picOutBufferDev);
ret = acldvppSetPicDescSize(picOutputDesc, dataSize);
ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, picOutputDesc, nullptr, nullptr);
restLen = restLen - 1;
}
反例代码片段:
aclError ret;
int restLen = 10;
uint32_t inBufferSize = 0;
void *g_picOutBufferDev;
void *inBufferDev = nullptr;
acldvppPicDesc *picOutputDesc;
size_t dataSize = (INPUT_WIDTH * INPUT_HEIGHT * 3) / 2;
// 申请一个picOutputDesc,每帧复用,且对Format、width、height、widthStride、heightStride参数值只设置了一次
picOutputDesc = acldvppCreatePicDesc();
ret = acldvppSetPicDescFormat(picOutputDesc, static_cast<acldvppPixelFormat>(1)); // 1:YUV420 semi-planner(nv12)
ret = acldvppSetPicDescWidth(picOutputDesc, 1920);
ret = acldvppSetPicDescHeight(picOutputDesc, 1080);
ret = acldvppSetPicDescWidthStride(picOutputDesc, 1920);
ret = acldvppSetPicDescHeightStride(picOutputDesc, 1080);
// read file to device memory
ReadFileToDeviceMem(filePath.c_str(), inBufferDev, inBufferSize);
while (restLen > 0) {
// 等待前一个使用picOutputDesc解码帧结束,重新复用picOutputDesc,但没有重新设置Format、width、height、widthStride、heightStride参数值
// 如果前一帧解码失败,picOutputDesc_的参数Format、width、height、widthStride、heightStride变成默认值0,
// width、height、widthStride、heightStride为0时,vdec会以实际图片宽高解码输出,但Format为0,表示YUV400格式,vdec不支持解码输出该格式,会导致本帧参数不合法解码失败
ret = acldvppMalloc(&g_picOutBufferDev, dataSize);
ret = acldvppSetPicDescData(picOutputDesc, g_picOutBufferDev);
ret = acldvppSetPicDescSize(picOutputDesc, dataSize);
ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, picOutputDesc, nullptr, nullptr);
restLen = restLen - 1;
}
父主题: FAQ
使用约束
表1 总体约束列表
| 分类 | 约束项 |
|---|---|
| 关于低功耗 | 进入系统休眠前,需要确保将正在运行的AI推理业务、媒体数据处理业务等进程退出。等待唤醒成功后,再继续执行业务。 |
| 关于进程 | - 不支持使用fork函数以及封装了fork的函数(如system、posix_spawnp等)创建多个子进程,且在进程中调用AscendCL接口的场景,否则进程运行时会报错或者卡死 。 - 对于Atlas 200/500 A2推理产品,一个Device上最多只能支持64个用户进程,Host最多只能支持Device个数*64个进程。 |
| 关于创建类和销毁类接口 | - 对于创建类接口(例如:aclrtCreateStream、aclrtCreateEvent、aclCreateDataBuffer等),用户调用该类接口创建对应的资源后,资源使用完成后,建议及时调用对应的销毁类接口(例如:aclrtDestroyStream、aclrtDestroyEvent、aclDestroyDataBuffer等),否则,程序可能会异常。 - 对于销毁类接口(例如:aclrtDestroyStream、aclrtDestroyEvent、aclrtFree、aclDestroyDataBuffer等),用户调用该类接口后,不能继续使用已释放或销毁的资源,建议用户调用销毁类接口后,将相关资源设置为无效值(例如,置为NULL)。 |
| 关于内存 | - 不支持在aclrtMemcpyAsync、aclrtMemsetAsync接口等异步操作内存过程中使用fork以及封装了fork的函数,如system、posix_spawnp等,否则会导致进程运行时会报错,甚至卡死等不可预期的错误。 - 使用AscendCL提供的内存申请接口(例如aclrtMalloc、acldvppMalloc等)申请内存后,为确保内存中不会有脏数据,建议在使用内存前先调用aclrtMemset或aclrtMemsetAsync接口先清空内存,例如aclrtMemset(devBufferPtr, devBufferSize, 0, devBufferSize)。 - Ascend RC形态下,如果应用程序中涉及aclrtMalloc、acldvppMalloc、hi_mpi_dvpp_malloc等内存申请接口,应用程序在Device上运行时,当前默认在内存不足时,应用程序可能会挂起,等待内存资源,用户可以根据实际需求选择启用操作系统提供的一些配置(例如,enable_oom_killer),这样在内存不足时,应用程序会自动退出,不会一直等待。 若启用enable_oom_killer,您需登录Device,在“/proc/sys/vm”目录下,以root用户启用enable_oom_killer,命令示例如下,1表示启用,0表示禁用: text<br> echo 1 > enable_oom_killer<br> |
| 旧版本昇腾AI处理器**->**新版本昇腾AI处理器的应用迁移 | 需在迁移后的昇腾AI处理版本上重新转换模型、编译应用程序,否则可能存在应用执行异常的情况。 |
父主题: 附录
表达约定
接口命名规则
接口命名同时满足如下规则:
- 规则1:acl+接口类别缩写+操作动词+对象
- 规则2:操作动词和对象均采用首字母大写
媒体数据处理V2版本下的接口命名规则例外,这一类接口命名以“hi_mpi”开头。
接口类别
| 接口类别 | 缩写 | 描述 |
|---|---|---|
| runtime | rt | 表示运行管理类的接口。 |
| DVPP | dvpp或vdec或venc | 表示媒体数据处理类的接口 |
| AIPP | aipp | 表示aipp(AI Preprocessing)类的接口 |
| CBLAS | blas | 表示blas类接口 |
| model | mdl | 表示模型推理类的接口 |
| graph | grph | 表示graph类的接口 |
| driver | drv | 表示驱动类的接口 |
| OP | op | 表示算子执行类的接口 |
| fv | fv | 表示特征向量检索接口 |
| Profiling | prof | 表示Profiling配置类接口 |
| tdt | tdt | 表示Tensor数据传输接口 |
注:
-
缩写原则上不超过4个字母。
-
在接口命名中,如果类别与操作对象重叠时,操作动词后的对象将省略。
如:aclmdlLoadFromFileWithMem,表示model类接口,这个接口表示含义是load model from file,因此在接口命名中Load后面 mdl将被省略。
变量命名
本文代码示例中涉及的变量,其中,命名带下划线的变量(例如:deviceId_)表示类的私有变量。
父主题: 附录
在线提单