CANN运行时Stream有序内存分配

CANN运行时Stream有序内存分配 11-10 Stream有序内存分配【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime本章节描述 CANN Runtime 的 Stream 有序内存分配SOMA接口用于内存池的创建、配置及异步内存分配与释放。aclError aclrtMemPoolCreate(aclrtMemPool *memPool, const aclrtMemPoolProps *poolProps)创建内存池。aclError aclrtMemPoolDestroy(const aclrtMemPool memPool)销毁通过aclrtMemPoolCreate接口创建的内存池。aclError aclrtMemPoolSetAttr(aclrtMemPool memPool, aclrtMemPoolAttr attr, void *value)设置属性值。aclError aclrtMemPoolGetAttr(aclrtMemPool memPool, aclrtMemPoolAttr attr, void *value)获取指定属性的值。aclError aclrtMemPoolMallocAsync(void **ptr, size_t size, aclrtMemPool memPool, aclrtStream stream)从内存池中申请指定大小的内存。异步接口。aclError aclrtMemPoolFreeAsync(void *ptr, aclrtStream stream)销毁通过aclrtMemPoolMallocAsync接口申请的内存仅将内存归还给内存池而不是实际释放物理内存。异步接口。aclError aclrtMemPoolTrimTo(aclrtMemPool memPool, size_t minBytesToKeep)收缩内存池保留指定大小的内存。Stream有序内存分配使用说明背景aclrtMalloc和aclrtFree是用于同步内存分配和管理的接口。以下示例代码展示了一个常见的内存使用场景使用aclrtMalloc申请内存通过异步拷贝的方式将内存数据拷贝到Device侧以备算子使用算子执行完成后通过Stream同步来确认该内存使用完毕然后使用aclrtFree释放内存。在异步操作较少的情况下这样的方式是可接受的。然而在有大量算子下发和对申请内存的高频异步操作时这种方式存在以下不足同步瓶颈在任务下发过程中如果需要进行内存分配或释放的调整容易产生同步瓶颈影响整体效率。累积延迟内存申请与释放本身耗时频繁操作会累积不必要的延迟进一步降低性能。#include acl/acl_rt.h #include acl/acl.h int main() { typedef struct { ...... } ArgsInfo; void *ptr0 nullptr; aclrtStream stream1; // 申请内存 aclrtMalloc(ptr0, sizeof(ArgsInfo), ACL_MEM_MALLOC_HUGE_FIRST); ...... // 配置任务下发 ArgsInfo usrArgs; // 拷贝信息到device侧申请内存 error aclrtMemcpyAsync(ptr0, sizeof(ArgsInfo), (void *)usrArgs, sizeof(ArgsInfo), ACL_MEMCPY_HOST_TO_DEVICE, stream1); // 下发任务 uint32_t blockDim 32; aclrtLaunchKernelV2(funcHandle, blockDim, (void *)usrArgs, sizeof(ArgsInfo), nullptr, stream1); // 流同步以同步释放申请内存 aclrtSynchronizeStream(stream1); // 释放内存释放前需要流同步 aclrtFree(ptr0); ...... // 流同步 aclrtSynchronizeStream(stream1); return 0; }相比之下本章所描述的Stream有序内存分配机制将内存分配与释放操作融入到Stream调度序列中以管理内存。这种方式将内存管理与Stream中的任务执行紧密结合无需显式同步Stream中的任务即可进行内存管理并且可依靠Stream本身的保序机制确保操作的有序执行。此外Runtime还提供内存复用的能力能够全面支持复杂的内存管理场景。#include acl/acl_rt.h #include acl/acl.h int main() { typedef struct { ...... } ArgsInfo; void *ptr0 nullptr; aclrtStream stream1; // 异步申请内存,testReusePool为用户创建的内存池 aclrtMemPoolMallocAsync(ptr0, sizeof(ArgsInfo), testReusePool, stream1); ...... // 配置任务下发 ArgsInfo usrArgs; // 拷贝信息到device侧申请内存 error aclrtMemcpyAsync(ptr0, sizeof(ArgsInfo), (void *)usrArgs, sizeof(ArgsInfo), ACL_MEMCPY_HOST_TO_DEVICE, stream1); // 下发任务 uint32_t blockDim 32; aclrtLaunchKernelV2(funcHandle, blockDim, (void *)usrArgs, sizeof(ArgsInfo), nullptr, stream1); // 异步释放内存无需进行流同步 aclrtMemPoolFreeAsync(ptr0, stream1); ...... // 流同步 aclrtSynchronizeStream(stream1); return 0; }内存复用机制调用aclrtMemPoolFreeAsync接口时仅将内存归还至内存池而不实际释放物理内存以便后续任务能够复用这些物理内存从而避免频繁申请和释放物理内存提升性能。复用内存时会根据本次任务所需的内存大小选择符合大小最接近的空闲内存。默认当内存池中空闲的物理内存超过指定阈值该阈值可通过aclrtMemPoolSetAttr接口配置默认值为0时在下一次Stream同步例如调用aclrtSynchronizeStream接口时系统将尝试真正释放空闲的物理内存。Runtime还提供了aclrtMemPoolTrimTo接口用户可以直接调用该接口主动收缩内存池释放空闲的物理内存。如果用户既通过aclrtMemPoolSetAttr接口配置了内存池中要保留的内存大小阈值对应ACL_RT_MEM_POOL_ATTR_RELEASE_THREAHOLD配置项又通过aclrtMemPoolTrimTo接口中的minBytesToKeep参数配置了要保留的物理内存大小那么后者具有更高的优先级。目前支持在一个Stream中复用内存也支持在两个Stream之间复用内存一个Stream内进行内存复用时基于下面的机制进行在执行某个Stream的任务时系统会查找该 Stream 中前序任务已归还到内存池中的内存并复用这些内存资源以提高内存利用率和减少内存分配的开销。两个Stream之间复用内存支持以下几种类型事件依赖内存复用在执行某个Stream的任务时系统会查找与该Stream通过Event关联的其他Stream并复用这些关联Stream中的任务已归还到内存池中的内存。此机制适用于用户应用程序中通过Event实现Stream间任务同步的场景。机会主义内存复用在执行某个Stream的任务时系统会检索内存池中可复用的内存但不保证内存复用一定成功。当内存复用失败时程序会报错停止。隐式依赖内存复用在执行某个Stream的任务时系统会检索内存池中可复用的内存。若这些内存曾被其他Stream使用但相关Stream之间不存在任务依赖关系则系统将自动实现相关Stream之间的同步等待以确保前一个Stream中的任务对内存的访问已经结束从而实现安全的内存复用。示例代码以下代码示例展现了应用异步内存申请与释放的场景结合aclrtLaunchKernelV2接口下发任务。代码仅做参考不能直接复制编译需要根据实际环境和需求进行调整。#include stdio.h #include string.h #include unistd.h #include time.h #include gtest/gtest.h #include acl/acl_rt.h #include acl/acl.h int main() { uint32_t devid 0; aclInit(NULL); aclrtSetDevice(devid); // 创建Context和Stream aclrtContext context; aclrtStream stream1; aclrtCreateContext(context, 0); aclrtCreateStream(stream1); // 设置内存池属性 aclrtMemLocation testLoc { 0, // id ACL_MEM_LOCATION_TYPE_DEVICE // type }; aclrtMemPoolProps testProp { ACL_MEM_ALLOCATION_TYPE_PINNED, // allocType ACL_MEM_HANDLE_TYPE_NONE, // handleType testLoc, // location 14UL 30, // maxSize 14GB 内存池大小为14G {0} // reserved }; // 创建内存池 aclrtMemPool testReusePool; auto ret aclrtMemPoolCreate(testReusePool, testProp); if (ret ! ACL_SUCCESS) { fprintf(stderr, Failed to create memory pool\n); return -1; } const size_t GB_TO_B 1024ULL * 1024 * 1024; // 定义算子信息结构体 typedef struct { void *input_x; void *input_y; void *output_z; } ArgsInfo; aclrtBinHandle bin_handle nullptr; aclrtFuncHandle func_handle; aclError aclrtBinaryGetFunction(binHandle, add_custom, funcHandle); void *ptr0 nullptr; void *ptr1 nullptr; void *ptr2 nullptr; void *ptr3 nullptr; // 异步申请内存 aclrtMemPoolMallocAsync(ptr1, sizeof(uint64_t), testReusePool, stream1); aclrtMemPoolMallocAsync(ptr2, sizeof(uint64_t), testReusePool, stream1); aclrtMemPoolMallocAsync(ptr3, sizeof(uint64_t), testReusePool, stream1); aclrtMemPoolMallocAsync(ptr0, sizeof(ArgsInfo), testReusePool, stream1); // 配置任务下发 ArgsInfo usrArgs; usrAgrs.input_x ptr1 usrAgrs.input_y ptr2; usrAgrs.output_z ptr3; error aclrtMemcpyAsync(devPtr, sizeof(ArgsInfo), (void *)usrArgs, sizeof(ArgsInfo), ACL_MEMCPY_HOST_TO_DEVICE, stream1); // 下发任务 uint32_t blockDim 32; aclrtLaunchKernelV2(funcHandle, blockDim, (void *)usrArgs, sizeof(ArgsInfo), nullptr, stream1); // 异步释放内存此前无需进行流同步 aclrtMemPoolFreeAsync(ptr0, stream1); aclrtMemPoolFreeAsync(ptr1, stream1); aclrtMemPoolFreeAsync(ptr2, stream1); aclrtMemPoolFreeAsync(ptr3, stream1); // 流同步 aclrtSynchronizeStream(stream1); // 销毁内存池、Stream和Context aclrtMemPoolDestroy(testReusePool); aclrtDestroyStream(stream1); aclrtDestroyContext(context); aclrtResetDevice(devid); aclFinalize(); return 0; }aclrtMemPoolCreateaclError aclrtMemPoolCreate(aclrtMemPool *memPool, const aclrtMemPoolProps *poolProps)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明创建内存池。参数说明参数名输入/输出说明memPool输出内存池实例。类型定义请参见aclrtMemPool。poolProps输入内存池配置。类型定义请参见aclrtMemPoolProps。返回值说明返回0表示成功返回其他值表示失败请参见aclError。aclrtMemPoolDestroyaclError aclrtMemPoolDestroy(const aclrtMemPool memPool)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明销毁通过aclrtMemPoolCreate接口创建的内存池。参数说明参数名输入/输出说明memPool输入内存池实例。类型定义请参见aclrtMemPool。返回值说明返回0表示成功返回其他值表示失败请参见aclError。aclrtMemPoolSetAttraclError aclrtMemPoolSetAttr(aclrtMemPool memPool, aclrtMemPoolAttr attr, void *value)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明设置属性值。多次对同一个内存池的同一个属性值进行设置以最后一次为准。参数说明参数名输入/输出说明memPool输入内存池实例。类型定义请参见aclrtMemPool。attr输入指定属性。类型定义请参见aclrtMemPoolAttr。value输入指向写入属性值地址的指针写入的数据其类型需要与attr处指定属性的类型相同。返回值说明返回0表示成功返回其他值表示失败请参见aclError。aclrtMemPoolGetAttraclError aclrtMemPoolGetAttr(aclrtMemPool memPool, aclrtMemPoolAttr attr, void *value)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明获取指定属性的值。如果未通过aclrtMemPoolSetAttr接口设置相应属性则获取该属性的默认值。参数说明参数名输入/输出说明memPool输入内存池实例。类型定义请参见aclrtMemPool。attr输入指定属性。类型定义请参见aclrtMemPoolAttr。value输出指向输出属性值地址的指针该指针指向的类型需与attr处指定属性的类型相同。返回值说明返回0表示成功返回其他值表示失败请参见aclError。aclrtMemPoolMallocAsyncaclError aclrtMemPoolMallocAsync(void **ptr, size_t size, aclrtMemPool memPool, aclrtStream stream)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明从内存池中申请指定大小的内存。异步接口。参数说明参数名输入/输出说明ptr输出指向待分配内存地址的指针。size输入待分配的内存大小单位Byte。memPool输入内存池实例。类型定义请参见aclrtMemPool。stream输入指定执行内存申请任务的Stream。类型定义请参见aclrtStream。返回值说明返回0表示成功返回其他值表示失败请参见aclError。aclrtMemPoolFreeAsyncaclError aclrtMemPoolFreeAsync(void *ptr, aclrtStream stream)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明销毁通过aclrtMemPoolMallocAsync接口申请的内存仅将内存归还给内存池而不是实际释放物理内存。异步接口。参数说明参数名输入/输出说明ptr输入指向待释放内存地址的指针。memPool输入指定执行内存释放任务的Stream。类型定义请参见aclrtStream。返回值说明返回0表示成功返回其他值表示失败请参见aclError。aclrtMemPoolFreeAsyncaclError aclrtMemPoolFreeAsync(void *ptr, aclrtStream stream)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明销毁通过aclrtMemPoolMallocAsync接口申请的内存仅将内存归还给内存池而不是实际释放物理内存。异步接口。参数说明参数名输入/输出说明ptr输入指向待释放内存地址的指针。memPool输入指定执行内存释放任务的Stream。类型定义请参见aclrtStream。返回值说明返回0表示成功返回其他值表示失败请参见aclError。aclrtMemPoolTrimToaclError aclrtMemPoolTrimTo(aclrtMemPool memPool, size_t minBytesToKeep)须知本接口为试验特性后续版本可能会存在变更不支持应用于商用产品中。产品支持情况产品是否支持Ascend 950PR/Ascend 950DT☓Atlas A3 训练系列产品/Atlas A3 推理系列产品☓Atlas A2 训练系列产品/Atlas A2 推理系列产品√功能说明收缩内存池保留指定大小的内存。在调用aclrtMemPoolFreeAsync接口释放内存时内存仅归还给内存池而不会实际释放物理内存这可能导致内存持续被占用进而使得aclrtMemPoolMallocAsync接口无法申请新的内存。此时可以调用aclrtMemPoolTrimTo接口主动收缩内存池释放未使用的物理内存而不影响当前正在使用的物理内存。参数说明参数名输入/输出说明memPool输入内存池实例。类型定义请参见aclrtMemPool。minBytesToKeep输入收缩内存池后内存池中要保留的物理内存大小单位Byte。返回值说明返回0表示成功返回其他值表示失败请参见aclError。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考