上一篇 AscendCL快速入门——运行资源管理篇(下)
主要介绍了关于AscendCL运行资源管理相关内容
本篇将介绍内存管理相关操作
一、概述
本文介绍了AscendCL内存管理的相关知识,帮助掌握AscendCL中“host”和”device”的概念与使用方法,能够在两侧分别申请和释放内存,并在两侧之间进行内存拷贝和数据传输。
我们用一般计算机进行举例,CPU+内存所在的一侧,或者说进程启动的一侧,收集数据的一侧,我们称之为”host”侧,GPU+显存所在的一侧,进行专用计算,使用数据,我们称之为”Decive“。
在华为设备中,将GPU换为NPU,将显存换为Device内存,CPU+内存是Host侧,NPU则是Device侧,数据在Host收集,并发送到Device侧供NPU进行推理,并将结果从Device侧发回Host侧进行后处理、使用等操作。但是,有些华为设备,进程就是在NPU上进行,数据也是在Device内存直接加载的,此时没有Host侧,也没有Host->Device的数据传输过程,这种情况下,内存管理简单,无需考虑Host侧的事情。
二、接口API与代码展示
Host侧内存管理相关接口方式比较简单,其中aclrtMallocHost是内存申请接口,aclrtFreeHost是其对应的释放接口aclError aclrtMallocHost(void **hostPtr, size_t size); aclError aclrtFreeHost(void *hostPtr);这里要注意:aclrtMallocHost申请出来的内存必须由aclrtFreeHost来释 放,而 aclrtFreeHost只能释放由aclrtMallocHost申请出来的内存。起来有点拗口,实际就是说aclrtMallocHost和aclrtFreeHost这两个接口是强对应的,不要用于别的内存。
阅读下段代码,理解内存管理接口的使用。
#pragma add_include_path(“/usr/local/Ascend/ascend-toolkit/latest/ x86_64-linux/acllib/include/”) #pragma add_library_path(“/usr/local/Ascend/ascend-toolkit/latest/ x86_64-linux/acllib/lib64/”) #pragma cling load(“libascendcl.so”) #define INFO_LOG(fmt, args…) fprintf(stdout, “[INFO] ” fmt “\n”, ##args) #define WARN_LOG(fmt, args…) fprintf(stdout, “[WARN] ” fmt “\n”, ##args) #define ERROR_LOG(fmt, args…) fprintf(stdout, “[ERROR] ” fmt “\n”, ##args) #include <iostream>> #include “acl/acl.h” #include <stdio.h> INFO_LOG(“AscendCL Hello World.”); // AscendCL init const char *aclConfigPath = “”; aclError ret = aclInit(aclConfigPath); if ((ret != ACL_ERROR_NONE) && (ret != ACL_ERROR_REPEAT_INITIALIZE)) { ERROR_LOG(“AscendCL init failed”); } INFO_LOG(“AscendCL init success.”); ret = aclrtSetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return 0; } void *hostInput = nullptr; int64_t size_input = 256; ret = aclrtMallocHost(&hostInput, size_input); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL HostMem Malloc failed %d .”, ret); } else { INFO_LOG(“AscendCL HostMem Malloc success .”); } if (hostInput != nullptr) { ret = aclrtFreeHost(hostInput); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL HostMem Free failed %d .”, ret); } else { INFO_LOG(“AscendCL HostMem Free success .”); } } ret = aclrtResetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return 0; } ret = aclFinalize(); if ((ret != ACL_ERROR_NONE)&& (ret != ACL_ERROR_REPEAT_FINALIZE)) { ERROR_LOG(“Finalize AscendCL failed.”); } INFO_LOG(“End to finalize AscendCL.”); return 0;申请内存接口又多个参数:policy,指明申请内存的策略,当前一共有三种策略可选ACL_MEM_MALLOC_HUGE_FIRST:当申请的内存小于等于1M时,即使使用该内存分配规则, 也是申请普通页的内存。当申请的内存大于1M时,优先申请大页内存,如果大页内存不够,则使用普通页的内存。
ACL_MEM_MALLOC_HUGE_ONLY:仅申请大页,如果大页内存 不够,则返回错误。
ACL_MEM_MALLOC_NORMAL_ONLY:仅申请普通页。
aclrtMalloc和aclrtFree要成对出现。用aclrtMalloc申请出来的内存也是对齐过的。
阅读如下代码,理解Device侧内存管理使用。
INFO_LOG(“AscendCL Hello World.”); // AscendCL init ret = aclInit(aclConfigPath); if ((ret != ACL_ERROR_NONE) && (ret != ACL_ERROR_REPEAT_INITIALIZE)) { ERROR_LOG(“AscendCL init failed.”); } else { INFO_LOG(“AscendCL init success.”); } ret = aclrtSetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return 0; } INFO_LOG(“Set device 0 success.”); void *devInput = nullptr; size_input = 256; ret = aclrtMalloc(&devInput, size_input, ACL_MEM_MALLOC_HUGE_FIRST); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL DeviceMem Malloc failed %d.”, ret); } else{ INFO_LOG(“AscendCL DeviceMem Malloc success.”); } if (hostInput != nullptr) { ret = aclrtFree(devInput); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL DeviceMem Free failed.”); } else{ INFO_LOG(“AscendCL DeviceMem Free success.”); } } ret = aclrtResetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return 0; } ret = aclFinalize(); if ((ret != ACL_ERROR_NONE)&& (ret != ACL_ERROR_REPEAT_FINALIZE)) { ERROR_LOG(“Finalize AscendCL failed.”); } INFO_LOG(“End to finalize AscendCL.”); return 0;两侧的内存都申请好了,下面该是对内存的初始化以及在Host与Device之 间拷贝 数据了。刚申请出来的内存,里边的数据是随机的,有时我们想要对其进行一波统一的 初始化,此时可以使用这个接口:
aclError aclrtMemset(void *devPtr, size_t maxCount, int32_t value, size_t count);
devPtr:内存的起始地址
maxCount:内存的最大长度,单位byte
value:设置的值需要设置为指定值的内存长度,单位Byte
接下来是内存拷贝,而拷贝这个动作又分“源地址”和“目的地址”,所以在 AscendCL的内存拷贝动作中,共有4种拷贝方向。内存拷贝的函数原型如下:
aclError aclrtMemcpy(void *dst, size_t destMax, const void *src, size_t count,aclrtMemcpyKind kind);
Dst:目的地址
destMax:目的内存地址的最大内存长度,单位Byte
src:源地址
count:内存复制的长度,单位Byte
kind:指向内存复制方向
阅读下面代码来理解本接口的使用
INFO_LOG(“AscendCL Hello World.”); // AscendCL init ret = aclInit(aclConfigPath); if ((ret != ACL_ERROR_NONE) && (ret != ACL_ERROR_REPEAT_INITIALIZE)) { ERROR_LOG(“AscendCL init failed.”); } INFO_LOG(“AscendCL init success.”); ret = aclrtSetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return 0; } hostInput = nullptr; devInput = nullptr; size_input = 256; ret = aclrtMallocHost(&hostInput, size_input); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL HostMem Malloc failed.”); } else{ INFO_LOG(“AscendCL HostMem Malloc success.”); } ret = aclrtMalloc(&devInput, size_input, ACL_MEM_MALLOC_HUGE_FIRST); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL DeviceMem Malloc failed %d.”,ret ); } else{ INFO_LOG(“AscendCL DeviceMem Malloc success .”); } ret = aclrtMemset(hostInput, size_input, 1, size_input); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL HostMem Memset failed %d.”,ret ); } else{ INFO_LOG(“AscendCL HostMem Memset success.”); } ret = aclrtMemset(devInput, size_input, 0, size_input); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL DeviceMem Memset failed %d.”,ret ); } else{ INFO_LOG(“AscendCL DeviceMem Memset success.”); } aclrtMemcpy(devInput, size_input, hostInput, size_input, ACL_MEMCPY_HOST_TO_DEVICE); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL Memcopy Host to Device failed %d.”,ret ); } else{ INFO_LOG(“AscendCL Memcopy Host to Device success.”); } if (hostInput != nullptr) { ret = aclrtFreeHost(hostInput); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL HostMem Free failed %d.”,ret ); } else{ INFO_LOG(“AscendCL HostMem Free success.”); } } if (devInput != nullptr) { ret = aclrtFree(devInput); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL DeviceMem Free failed %d.”,ret ); } else{ INFO_LOG(“AscendCL DeviceMem Free success.”); } } ret = aclrtResetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return 0; } ret = aclFinalize(); if ((ret != ACL_ERROR_NONE)&& (ret != ACL_ERROR_REPEAT_FINALIZE)) { ERROR_LOG(“Finalize AscendCL failed.”); } INFO_LOG(“End to finalize AscendCL.”); return 0;aclError aclrtGetMemInfo(aclrtMemAttr attr, size_t *free, size_t *total)
其中的attr参数指的是内存的类型,主要有以下几种:
ACL_DDR_MEM, //DDR内存,DDR上所有大页内存+普通内存
ACL_DDR_MEM_HUGE, //DDR大页内存
ACL_DDR_MEM_NORMAL, //DDR普通内存
选择内存类型的时候要根据当前使用的设备形态来选择,阅读以下代码
INFO_LOG(“AscendCL Hello World.”); // AscendCL init ret = aclInit(aclConfigPath); if ((ret != ACL_ERROR_NONE) && (ret != ACL_ERROR_REPEAT_INITIALIZE)) { ERROR_LOG(“AscendCL init failed.”); return ret; } INFO_LOG(“AscendCL init success.”); ret = aclrtSetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return ret; } size_t free_mem = 0; size_t total_mem = 0; ret = aclrtGetMemInfo(ACL_DDR_MEM, &free_mem, &total_mem); if (ret != ACL_SUCCESS) { ERROR_LOG(“AscendCL GetMemInfo failed %d.”,ret); return ret; } INFO_LOG(“available memory: %zu”, free_mem); INFO_LOG(“total memory: %zu”, total_mem); ret = aclrtResetDevice(0); if (ret != ACL_ERROR_NONE) { ERROR_LOG(“AclrtSetDevice failed %d.”,ret); return ret; } ret = aclFinalize(); if ((ret != ACL_ERROR_NONE)&& (ret != ACL_ERROR_REPEAT_FINALIZE)) { ERROR_LOG(“Finalize AscendCL failed.”); return ret; } INFO_LOG(“End to finalize AscendCL.”); return 0;aclError aclrtGetRunMode(aclrtRunMode *runMode);
runMode参数是接口调用后输出的一个枚举值,有两种可能:
ACL_DEVICE, //昇腾AI软件栈运行在Device的Control CPU或板端环境上
ACL_HOST, //昇腾AI软件栈运行在Host CPU上。
下篇将介绍 AscendCL入门 —— 模型推理篇
免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:AscendCL快速入门——内存管理篇-asgard内存条和金士顿内存条 https://www.yhzz.com.cn/a/8776.html