数据传输的关键接口调用流程如下:
Ascend RC场景下,不涉及Host上的内存申请、Host内的数据传输、Host与Device之间的数据传输。
需先调用aclrtGetRunMode接口获取软件栈的运行模式,当查询结果为ACL_HOST,则数据传输时涉及申请Host上的内存;当查询结果为ACL_DEVICE,则数据传输时不涉及申请Host上的内存,仅需申请Device上的内存。该种方式多一些代码逻辑的判断,不需要由用户处理Device上的内存对齐。在Device上运行应用的场景,该种方式少一些内存复制的步骤,性能较好。
无需调用aclrtGetRunMode接口获取软件栈的运行模式。该种方式代码逻辑相比前一种简单,但需由用户处理Device上的内存对齐。
当前支持调用aclrtMemcpy接口执行同步Host内的内存复制任务,不支持调用aclrtMemcpyAsync接口执行异步Host内的内存复制功能,若调用aclrtMemcpyAsync接口时选择ACL_MEMCPY_HOST_TO_HOST类型时,由于是异步接口,虽然接口调用成功,下发了内存复制任务,但在调用aclrtSynchronizeStream接口等待该任务执行时会返回失败。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1. 申请内存 uint64_t size = 1 * 1024 * 1024; void* hostPtrA = NULL; void* hostPtrB = NULL; aclrtMallocHost(&hostPtrA, size); aclrtMallocHost(&hostPtrB, size); // 2. 申请内存后,可向内存中读入数据,该自定义函数ReadFile由用户实现 ReadFile(fileName, hostPtrA, size); // 3. 内存复制,可以选择同步 // 同步内存复制,hostPtrA表示Host上源内存地址指针,hostPtrB表示Host上目的内存地址指针,size表示内存大小 aclrtMemcpy(hostPtrB, size, hostPtrA, size, ACL_MEMCPY_HOST_TO_HOST); // 4. 使用完内存中的数据后,需及时释放资源 aclrtFreeHost(hostPtrA); aclrtFreeHost(hostPtrB); // ......
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1. 申请内存 uint64_t size = 1 * 1024 * 1024; void* hostPtrA = NULL; void* devPtrB = NULL; aclrtMallocHost(&hostPtrA, size); aclrtMalloc(&devPtrB, size, ACL_MEM_MALLOC_NORMAL_ONLY); // 2. 申请内存后,可向内存中读入数据,该自定义函数ReadFile由用户实现 ReadFile(fileName, hostPtrA, size); // 3. 内存复制,可以选择同步 // 同步内存复制,hostPtrA表示Host上源内存地址指针,devPtrB表示Device上目的内存地址指针,size表示内存大小 aclrtMemcpy(devPtrB, size, hostPtrA, size, ACL_MEMCPY_HOST_TO_DEVICE); // 4. 使用完内存中的数据后,需及时释放资源 aclrtFreeHost(hostPtrA); aclrtFree(devPtrB); // ......
// 1. 申请内存 uint64_t size = 1 * 1024 * 1024; void* hostAddr = NULL; void* devAddr = NULL; // 由于异步内存复制时,要求首地址64字节对齐,因此申请内存时,size需加64 aclrtMallocHost(&hostAddr, size + 64); // 通过aclrtMalloc接口申请的内存,系统已保证内存地址64字节对齐,无需用户处理对齐的逻辑 aclrtMalloc(&devAddr, size, ACL_MEM_MALLOC_NORMAL_ONLY); // 2. 异步内存复制 aclrtStream stream = NULL; aclrtCreateStream(&stream); // 获取到64字节对齐的地址 char *hostAlignAddr =(char *)hostAddr + 64 - ((uintptr_t)hostAddr % 64); // 申请内存后,可向内存中读入数据,该自定义函数ReadFile由用户实现 ReadFile(fileName, hostAlignAddr, size); aclrtMemcpyAsync(devAddr, size, hostAlignAddr, size, ACL_MEMCPY_HOST_TO_DEVICE, stream); aclrtSynchronizeStream(stream); // 3. 释放资源 aclrtDestroyStream(stream); aclrtFreeHost(hostAddr); aclrtFree(devAddr); // ......
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1. 申请内存 uint64_t size = 1 * 1024 * 1024; void* devPtrA = NULL; void* hostPtrB = NULL; aclrtMalloc(&devPtrA, size, ACL_MEM_MALLOC_NORMAL_ONLY); aclrtMallocHost(&hostPtrB, size); // 2. 申请内存后,可向内存中读入数据,该自定义函数ReadFile由用户实现 ReadFile(fileName, devPtrA, size); // 3. 内存复制,可以选择同步 // 同步内存复制,devPtrA表示Device上源内存地址指针,hostPtrB表示Host上目的内存地址指针,size表示内存大小 aclrtMemcpy(hostPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_HOST); // 4. 使用完内存中的数据后,需及时释放资源 aclrtFree(devPtrA); aclrtFreeHost(hostPtrB); // ......
// 1. 申请内存 uint64_t size = 1 * 1024 * 1024; void* hostAddr = NULL; void* devAddr = NULL; // 由于异步内存复制时,要求首地址64字节对齐,因此申请内存时,size需加64 aclrtMallocHost(&hostAddr, size + 64); // 通过aclrtMalloc接口申请的内存,系统已保证内存地址64字节对齐,无需用户处理对齐的逻辑 aclrtMalloc(&devAddr, size, ACL_MEM_MALLOC_NORMAL_ONLY); // 2. 申请内存后,可向内存中读入数据,该自定义函数ReadFile由用户实现 ReadFile(fileName, devAddr, size); // 3. 异步内存复制 aclrtStream stream = NULL; aclrtCreateStream(&stream); // 获取到64字节对齐的地址 char *hostAlignAddr =(char *)hostAddr + 64 - ((uintptr_t)hostAddr % 64); aclrtMemcpyAsync(hostAlignAddr, size, devAddr, size, ACL_MEMCPY_DEVICE_TO_HOST, stream); aclrtSynchronizeStream(stream); // 4. 释放资源 aclrtDestroyStream(stream); aclrtFreeHost(hostAddr); aclrtFree(devAddr); // ......
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1. 申请内存 uint64_t size = 1 * 1024 * 1024; void* devPtrA = NULL; void* devPtrB = NULL; aclrtMalloc(&devPtrA, size, ACL_MEM_MALLOC_NORMAL_ONLY); aclrtMalloc(&devPtrB, size, ACL_MEM_MALLOC_NORMAL_ONLY); // 2. 申请内存后,可向内存中读入数据,该自定义函数ReadFile由用户实现 ReadFile(fileName, devPtrA, size); // 3. 内存复制,可以选择同步或异步 // 同步内存复制,devPtrA表示Device上源内存地址指针,devPtrB表示Device上目的内存地址指针,size表示内存大小 aclrtMemcpy(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE); // 异步内存复制 aclrtStream stream = NULL; aclrtCreateStream(&stream); aclrtMemcpyAsync(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE, stream); aclrtSynchronizeStream(stream); // 4. 使用完内存中的数据后,需及时释放资源 aclrtDestroyStream(stream); aclrtFree(devPtrA); aclrtFree(devPtrB); // ......
Atlas 200/300/500 推理产品上,不支持该功能。
注意点说明:
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
int main(int argc, const char *argv[]) { // AscendCL初始化 auto ret = aclInit(NULL); int32_t canAccessPeer = 0; // 查询Device 0和Device 1之间是否支持内存复制 ret = aclrtDeviceCanAccessPeer(&canAccessPeer, 0, 1); // 1表示支持内存复制 if (canAccessPeer == 1) { // ************************************************************ // Device 0下的操作 ret = aclrtSetDevice(0); void *dev0; ret = aclrtMalloc(&dev0, 10, ACL_MEM_MALLOC_HUGE_FIRST_P2P); ret = aclrtMemset(dev0, 10, 1, 10); // ************************************************************ // Device 1下的操作,使能当前Device(Device 1)到指定Device(Device 0)的内存复制,当前Device通过aclrtSetDevice接口设置,指定Device在aclrtDeviceEnablePeerAccess接口的第一个参数指定 ret = aclrtSetDevice(1); ret = aclrtDeviceEnablePeerAccess(0, 0); void *dev1; ret = aclrtMalloc(&dev1, 10, ACL_MEM_MALLOC_HUGE_FIRST_P2P); ret = aclrtMemset(dev1, 10, 0, 10); // 执行复制,将Device 0上的内存数据复制到Device 1上 ret = aclrtMemcpy(dev1, 10, dev0, 10, ACL_MEMCPY_DEVICE_TO_DEVICE); ret = aclrtResetDevice(1); // ************************************************************ // ************************************************************ // Device 0下的操作,等Device 1下的复制操作完成后,再调用aclrtResetDevice接口释放Device 0的资源 ret = aclrtSetDevice(0); ret = aclrtResetDevice(0); // ************************************************************ printf("P2P copy success\n"); } else { printf("current device doesn't support p2p feature\n"); } // AscendCL去初始化 aclFinalize(); return 0; }