AscendCL快速入门——内存管理篇

AscendCL快速入门——内存管理篇 四叶草~ 2023-04-12 18:58:28 1628

上一篇 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;
  • Device侧内存管理
aclError aclrtMalloc(void **devPtr, size_t size, aclrtMemMallocPolicy policy);
aclError aclrtFree(void *devPtr);

申请内存接口又多个参数: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入门 —— 模型推理篇

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
四叶草~
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区