CANN/runtime内存管理指南 内存管理【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime内存管理总述在昇腾异构计算架构中系统由主机Host和设备Device组成。Host和Device各自拥有独立的内存Host内存是指AI处理器所在服务器的主机内存即CPU内存而Device内存则是指AI处理器自带的设备内存。内存管理中要做好的两件事是**可以访问内存**Runtime提供了一套内存管理API使开发者能够高效便捷地编写应用程序中的内存管理代码。由于Host和Device的内存相互独立Runtime提供了专门的接口来分别申请和释放Host内存及Device内存。例如申请和释放Host内存的接口为aclrtMallocHost和aclrtFreeHost而申请和释放Device内存的接口为aclrtMalloc和aclrtFree。**高效访问内存**为了实现最佳的内存访问性能需要将数据存储在对应的内存中例如算子在Device上执行过程中访问Device上的数据性能要远高于访问Host侧的。为此Runtime提供了Host与Device之间互相拷贝内存的接口支持同步和异步方式例如aclrtMemcpy和aclrtMemcpyAsync等以便开发者更好地规划数据的存储与访问。Device内存使用在昇腾异构计算编程中典型的使用场景是通过aclrtMallocHost接口申请Host内存通过aclrtMalloc接口申请Device内存通过aclrtMemcpy同步/aclrtMemcpyAsync异步接口将数据从Host拷贝到Device上算子执行过程中使用Device内存进行计算并保存结果。以下是一段简单的示例代码。在示例代码中两个张量从Host内存被拷贝到Device内存在Device侧完成计算再将结果从Device内存拷贝到Host内存int main(void) { int32_t deviceId 0; int64_t N 16; const size_t bytes static_castsize_t(N) * sizeof(float); aclrtStream stream nullptr; // STEP 1: 初始化、Stream创建 aclInit(nullptr); aclrtSetDevice(deviceId); aclrtCreateStream(stream); // STEP 2: 申请Host内存 void* hostA nullptr; void* hostB nullptr; void* hostOut nullptr; aclrtMallocHost(hostA, bytes); aclrtMallocHost(hostB, bytes); aclrtMallocHost(hostOut, bytes); // 输入数据初始化 ... // STEP 3: 申请Device内存 void* deviceA nullptr; void* deviceB nullptr; void* deviceOut nullptr; // 第三个参数aclrtMemMallocPolicy表示申请内存时的内存页分配策略 // ACL_MEM_MALLOC_HUGE_FIRST 表示大页内存优先其余定义参考API文档 aclrtMalloc(deviceA, bytes, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(deviceB, bytes, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(deviceOut, bytes, ACL_MEM_MALLOC_HUGE_FIRST); // STEP 4: 将输入数据从Host内存传输到Device内存 aclrtMemcpy(deviceA, bytes, hostA, bytes, ACL_MEMCPY_HOST_TO_DEVICE); aclrtMemcpy(deviceB, bytes, hostB, bytes, ACL_MEMCPY_HOST_TO_DEVICE); // STEP 5: 执行计算操作比如aclnnAdd将计算结果保存在Device内存 // ... // STEP 6: 将计算结果从Device内存传输到Host内存 aclrtMemcpy(hostOut, bytes, deviceOut, bytes, ACL_MEMCPY_DEVICE_TO_HOST); // STEP 7: 在Host侧处理计算结果 // ... // STEP 8: 资源清理 // STEP 8.1: 释放Host和Device内存 if (deviceA) (void)aclrtFree(deviceA); if (deviceB) (void)aclrtFree(deviceB); if (deviceOut) (void)aclrtFree(deviceOut); if (hostA) (void)aclrtFreeHost(hostA); if (hostB) (void)aclrtFreeHost(hostB); if (hostOut) (void)aclrtFreeHost(hostOut); // STEP 8.2: 清理设备和进程资源 if (stream) (void)aclrtDestroyStream(stream); (void)aclrtResetDevice(deviceId); (void)aclFinalize(); return 0; }Host锁页内存使用在CANN编程框架中Host内存可以是Pageable内存也可以是Page-Locked内存也称锁页内存或Pinned内存Pageable内存由操作系统统一管理。开发者可使用malloc、mmap等传统接口申请内存使用free、munmap等接口释放内存。当内存压力较大时Pageable内存会被换出到后备存储提供的交换空间中。当Pageable内存中的数据被传输到Device时数据首先会被复制到缓冲区然后通过DMADirect Memory Access通道传输到Device。Page-Locked内存即锁页内存。开发者需要用Runtime提供的API进行锁页内存的申请和释放例如aclrtMallocHost、aclrtFreeHost等接口。对于锁页内存虚拟页与物理页的映射关系固定在其生命周期内不会被换出至交换空间。当锁页内存中的数据被传输至Device时直接通过DMA通道传输无需经过缓冲区传输性能更优。在Runtime中使用锁页内存的好处如下设备可以直接通过DMA访问主机内存无需经过缓冲区可以提供更好的传输性能数据搬运过程无需CPU参与可以实现数据的异步传输数据的异步传输使传输过程和计算过程可以相互掩盖减少传输计算的整体时长。锁页内存可直接通过aclrtMallocHost接口申请示例代码如下。如果需要在申请时指定内存的其他配置例如自定义模块ID、配置VAvirtual address一致性等也可以使用aclrtMallocHostWithCfg接口申请。// 资源初始化 ...... // 申请锁页内存 void *hostPtr NULL; aclrtMallocHost(hostPtr, size); // 申请Device内存 void *devicePtr NULL; aclrtMalloc(devicePtr, size, ACL_MEM_MALLOC_NORMAL_ONLY); // 内存初始化 ...... // 异步H2D aclrtMemcpyAsync(devicePtr, size, hostPtr, size, ACL_MEMCPY_HOST_TO_DEVICE, stream); // 等待异步拷贝完成 aclrtSynchronizeStream(stream); // 资源释放 if (devicePtr) (void)aclrtFree(devicePtr); if (hostPtr) (void)aclrtFreeHost(hostPtr); ......若使用malloc/mmap等内存管理接口申请Pageable内存当前Runtime提供了aclrtHostRegisterV2接口用于将Pageable内存转换为锁页内存供Device访问。请注意当OS内核版本为5.10或更低时此方法会导致异常此时应通过aclrtMallocHost接口申请锁页内存。// 申请Pageable内存 void *hostPtr malloc(size); // 注册为锁页内存(ACL_HOST_REG_PINNED)并映射到Device(ACL_HOST_REG_MAPPED) aclrtHostRegisterV2(hostPtr, size, ACL_HOST_REG_PINNED|ACL_HOST_REG_MAPPED); // 获取Device地址 void *devicePtr NULL; aclrtHostGetDevicePointer(hostPtr, devicePtr, 0); // 任务通过Device地址访问对应内存 ... // 资源释放 aclrtHostUnregister(hostPtr); free(hostPtr);若涉及Host内存的VAvirtual address一致性Host内存可以通过aclrtHostRegister接口注册到Device根据Host指针映射得到Device指针供Device使用默认情况下对于同一块Host内存Host和Device看到的虚拟地址是不同的如果需要Host和Device的虚拟地址保持一致可以使用aclrtMallocHostWithCfg接口申请锁页内存并在aclrtMallocConfig参数中指定ACL_RT_MEM_ATTR_VA_FLAG属性后续再注册获取到的Device虚拟地址即可与Host虚拟地址一致。虚拟内存管理Runtime提供了一套虚拟内存管理的API接口可供开发者更加精细化的管理其内存例如可以提前申请好大片的连续的虚拟内存以简化后续的管理使用同时可以按需在使用过程中申请物理内存并与虚拟内存做地址映射即可以精细化管理也可以最大化利用物理内存此外还可以在进程之间利用虚拟内存管理中的handle来实现物理内存共享。以下是一个虚拟内存的基本使用示例申请好物理内存以及虚拟内存后通过aclrtMapMem接口进行映射即可通过虚拟地址访问对应的数据void SampleTest() { // 1. 查询内存申请粒度并对size做对齐处理 // 设置要申请的物理内存属性 aclrtPhysicalMemProp prop {}; prop.handleType ACL_MEM_HANDLE_TYPE_NONE; // handle类型当前仅支持 ACL_MEM_HANDLE_TYPE_NONE prop.allocationType ACL_MEM_ALLOCATION_TYPE_PINNED; // 内存分配类型当前仅支持分配锁页内存 prop.memAttr ACL_HBM_MEM_NORMAL; // 内存属性这里申请HBM内存 prop.location.type ACL_MEM_LOCATION_TYPE_DEVICE; // 内存所在位置这里申请Device内存且deviceId为0 prop.location.id 0; // 查询申请最小内存粒度 size_t granularity 0UL; aclrtMemGetAllocationGranularity(prop, ACL_RT_MEM_ALLOC_GRANULARITY_MINIMUM, granularity); // 根据查询到的内存申请粒度做内存对齐以便节约内存 const size_t dataSize 1024 * sizeof(float); size_t alignedSize ((dataSize granularity - 1U) / granularity) * granularity; // 2. 申请物理内存 // 获取存放物理内存信息的handle用于后续申请虚拟内存使用需要注意的是handle并不能直接当虚拟地址访问 aclrtDrvMemHandle handle nullptr; aclrtMallocPhysical(handle, alignedSize, prop, 0); // 最后一个flag参数当前默认填0 // 3. 申请预留虚拟内存 void *virPtr; // virPtr即为申请预留的虚拟地址指针alignedSize为申请预留的内存大小 aclrtReserveMemAddress(virPtr, alignedSize, 0, nullptr, 0); // 4. 映射虚拟内存到物理内存上 // 将申请物理内存时获取的handle与申请预留的虚拟地址空间关联起来 // 需要注意的是映射的内存大小alignedSize要与申请物理内存时设置的size大小一致 aclrtMapMem(virPtr, alignedSize, 0, handle, 0); // 5. 设置虚拟内存访问权限 aclrtMemAccessDesc accessDesc {}; accessDesc.flags ACL_RT_MEM_ACCESS_FLAGS_READWRITE; accessDesc.location.type ACL_MEM_LOCATION_TYPE_DEVICE; accessDesc.location.id 0; aclrtMemSetAccess(virPtr, alignedSize, accessDesc, 1); // 6. 内存访问 // 这里以Device-Host的内存拷贝为例展示正常访问映射后的内存地址 int *hostPtr; aclrtMallocHost(reinterpret_castvoid**(hostPtr), alignedSize); aclrtMemcpy(hostPtr, alignedSize, virPtr, alignedSize, ACL_MEMCPY_DEVICE_TO_HOST); aclrtFreeHost(hostPtr); // 7. 取消虚拟内存与物理内存的映射 aclrtUnmapMem(virPtr); // 8. 释放虚拟内存 aclrtReleaseMemAddress(virPtr); // 9. 释放物理内存 aclrtFreePhysical(handle); }【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考