上海网站定制价格低,深圳网站建设黄浦网络 骗钱,开发app软件公司哪家好,wordpress 动态主题文章目录 【2024第一期CANN训练营】3、AscendCL运行时管理1. 初始化与去初始化2. 资源申请与释放2.1 申请流程2.2 释放流程2.3 运行模式#xff08;可选#xff09; 3. 数据传输3.1 接口调用流程3.2 主要数据传输场景1. Host内的数据传输2. 从Host到Device的数据传输3. 从Dev… 文章目录 【2024第一期CANN训练营】3、AscendCL运行时管理1. 初始化与去初始化2. 资源申请与释放2.1 申请流程2.2 释放流程2.3 运行模式可选 3. 数据传输3.1 接口调用流程3.2 主要数据传输场景1. Host内的数据传输2. 从Host到Device的数据传输3. 从Device到Host的数据传输4. 一个Device内的数据传输5. 两个Device间的数据传输 4. Stream管理4.1 单线程单Stream4.2 单线程多Stream4.3 多线程多Stream 5. 多Device切换5.1 多Device切换关键接口5.2 多Device切换流程 6. 同步等待6.1 Event的同步等待6.2 Stream内任务的同步等待6.3 Stream间任务的同步等待6.4 Device的同步等待 【2024第一期CANN训练营】3、AscendCL运行时管理
本教程将介绍如何使用昇腾社区AscendCL应用开发接口进行运行时管理。并从AscendCL的初始化与去初始化、运行管理资源的申请与释放、数据传输、Stream管理、多Device切换以及同步等待等关键步骤来展开内容
以下教程涉及的所有代码都可以从样例介绍中获得
1. 初始化与去初始化
在使用AscendCL进行开发前首先需要初始化AscendCL环境。这可以通过调用aclInit接口来完成。如果默认配置已满足需求可以直接传入NULL。
aclError ret aclInit(NULL);完成所有AscendCL调用后需要调用aclFinalize接口去初始化AscendCL。
ret aclFinalize();2. 资源申请与释放
运行管理资源包括Device、Context和Stream。申请和释放这些资源的顺序很重要。
2.1 申请流程
使用aclrtSetDevice显式指定运算的Device。通过aclrtCreateContext创建Context。使用aclrtCreateStream创建Stream。
示例代码
// 初始化变量
int32_t deviceId0 ;
aclrtContext context;
aclrtStream stream;aclError ret aclrtSetDevice(deviceId);
ret aclrtCreateContext(context, deviceId);
ret aclrtCreateStream(stream);2.2 释放流程
使用aclrtDestroyStream销毁Stream。通过aclrtDestroyContext销毁Context。调用aclrtResetDevice重置Device。
示例代码
ret aclrtDestroyStream(stream);
ret aclrtDestroyContext(context);
ret aclrtResetDevice(deviceId);2.3 运行模式可选
获取当前昇腾AI软件栈的运行模式根据不同的运行模式后续的接口调用方式不同
如果查询结果为ACL_HOST则数据传输时涉及申请Host上的内存。如果查询结果为ACL_DEVICE则数据传输时仅需申请Device上的内存。
aclrtRunMode runMode;
extern bool g_isDevice;ret aclrtGetRunMode(runMode);
g_isDevice (runMode ACL_DEVICE);3. 数据传输
数据传输包含申请内存将数据读入内存内存复制三个环节
3.1 接口调用流程
申请内存 在Host上申请内存可以使用C标准库中的new或malloc或者使用AscendCL提供的aclrtMallocHost接口。在Device上申请内存则需要使用aclrtMalloc接口。 将数据读入内存用户需要自行管理数据读入内存的实现逻辑。内存复制通过内存复制实现数据传输可以选择同步或异步内存复制。 同步内存复制使用aclrtMemcpy接口异步内存复制使用aclrtMemcpyAsync接口并配合aclrtSynchronizeStream接口实现同步等待。
3.2 主要数据传输场景
1. Host内的数据传输
当前仅支持调用aclrtMemcpy接口执行同步Host内的内存复制任务不支持调用aclrtMemcpyAsync接口执行异步Host内的内存复制功能
// 申请内存
uint64_t size 1 * 1024 * 1024;
void* hostPtrA NULL;
void* hostPtrB NULL;
aclrtMallocHost(hostPtrA, size);
aclrtMallocHost(hostPtrB, size);// 向内存中读入数据
ReadFile(fileName, hostPtrA, size);// 同步内存复制
aclrtMemcpy(hostPtrB, size, hostPtrA, size, ACL_MEMCPY_HOST_TO_HOST);// 释放资源
aclrtFreeHost(hostPtrA);
aclrtFreeHost(hostPtrB);2. 从Host到Device的数据传输
既支持同步内存复制又支持异步内存复制
同步内存复制
// 申请内存
uint64_t size 1 * 1024 * 1024;
void* hostPtrA NULL;
void* devPtrB NULL;
aclrtMallocHost(hostPtrA, size);
aclrtMalloc(devPtrB, size, ACL_MEM_MALLOC_NORMAL_ONLY);// 向内存中读入数据
ReadFile(fileName, hostPtrA, size);// 同步内存复制
aclrtMemcpy(devPtrB, size, hostPtrA, size, ACL_MEMCPY_HOST_TO_DEVICE);// 释放资源
aclrtFreeHost(hostPtrA);
aclrtFree(devPtrB);异步内存复制
// 申请内存
uint64_t size 1 * 1024 * 1024;
void* hostAddr NULL;
void* devAddr NULL;
aclrtMallocHost(hostAddr, size 64);
aclrtMalloc(devAddr, size, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtStream stream NULL;
aclrtCreateStream(stream);// 获取到64字节对齐的地址
char *hostAlignAddr (char *)hostAddr 64 - ((uintptr_t)hostAddr % 64);// 向内存中读入数据
ReadFile(fileName, hostAlignAddr, size);// 异步内存复制
aclrtMemcpyAsync(devAddr, size, hostAlignAddr, size, ACL_MEMCPY_HOST_TO_DEVICE, stream);
aclrtSynchronizeStream(stream);// 释放资源
aclrtDestroyStream(stream);
aclrtFreeHost(hostAddr);
aclrtFree(devAddr);3. 从Device到Host的数据传输
既支持同步内存复制又支持异步内存复制
同步内存复制
// 申请内存
uint64_t size 1 * 1024 * 1024;
void* devPtrA NULL;
void* hostPtrB NULL;
aclrtMalloc(devPtrA, size, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtMallocHost(hostPtrB, size);// 向内存中读入数据
ReadFile(fileName, devPtrA, size);// 同步内存复制
aclrtMemcpy(hostPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_HOST);// 释放资源
aclrtFree(devPtrA);
aclrtFreeHost(hostPtrB);异步内存复制
// 申请内存
uint64_t size 1 * 1024 * 1024;
void* hostAddr NULL;
void* devAddr NULL;
aclrtMallocHost(hostAddr, size 64);
aclrtMalloc(devAddr, size, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtStream stream NULL;
aclrtCreateStream(stream);// 向内存中读入数据
ReadFile(fileName, devAddr, size);// 获取到64字节对齐的地址
char *hostAlignAddr (char *)hostAddr 64 - ((uintptr_t)hostAddr % 64);// 异步内存复制
aclrtMemcpyAsync(hostAlignAddr, size, devAddr, size, ACL_MEMCPY_DEVICE_TO_HOST, stream);
aclrtSynchronizeStream(stream);// 释放资源
aclrtDestroyStream(stream);
aclrtFreeHost(hostAddr);
aclrtFree(devAddr);4. 一个Device内的数据传输
// 申请内存
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);// 向内存中读入数据
ReadFile(fileName, devPtrA, size);// 同步内存复制
aclrtMemcpy(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE);// 异步内存复制
aclrtStream stream;
aclrtCreateStream(stream);
aclrtMemcpyAsync(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE, stream);
aclrtSynchronizeStream(stream);// 释放资源
aclrtFree(devPtrA);
aclrtFree(devPtrB);5. 两个Device间的数据传输
Atlas 200/300/500 推理产品上不支持该功能。Atlas 200/500 A2推理产品不支持该功能。
// AscendCL初始化
auto ret aclInit(NULL);// 查询Device 0和Device 1之间是否支持内存复制
int32_t canAccessPeer 0;
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下的操作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);ret aclrtSetDevice(0);ret aclrtResetDevice(0);printf(P2P copy success\n);
} else {printf(current device doesnt support p2p feature\n);
}// AscendCL去初始化
aclFinalize();注意事项
在进行数据传输时需要确保内存申请和释放的正确性避免内存泄漏。异步内存复制时需要确保内存地址的64字节对齐。在使用AscendCL进行数据传输时应该增加异常处理逻辑以便于及时发现并解决问题。
4. Stream管理
在AscendCL应用开发中Stream是任务队列的抽象用于管理任务的并行执行。理解并有效管理Stream对于提升程序性能和资源利用率至关重要。AscendCL提供了以下几种Stream管理机制
4.1 单线程单Stream
在单线程环境下可以创建一个Stream来管理任务的执行。
#include acl/acl.h// 显式创建一个Stream
aclrtStream stream;
aclrtCreateStream(stream);// 调用触发任务的接口传入stream参数
aclrtMemcpyAsync(dstPtr, dstSize, srcPtr, srcSize, ACL_MEMCPY_HOST_TO_DEVICE, stream);// 调用aclrtSynchronizeStream接口阻塞应用程序运行直到指定Stream中的所有任务都完成。
aclrtSynchronizeStream(stream);// Stream使用结束后显式销毁Stream
aclrtDestroyStream(stream);4.2 单线程多Stream
在单线程环境下可以创建多个Stream来并行执行不同的任务。
#include acl/acl.h
int32_t deviceId 0;
uint32_t modelId1 0;
uint32_t modelId2 1;// 显式创建一个Stream
aclrtContext context;
aclrtStream stream1, stream2;// 创建Context
aclrtCreateContext(context, deviceId);// 创建stream1
aclrtCreateStream(stream1);
// 调用触发任务的接口例如异步模型推理任务下发在stream1
aclmdlDataset *input1, *output1;
aclmdlExecuteAsync(modelId1, input1, output1, stream1);// 创建stream2
aclrtCreateStream(stream2);
// 调用触发任务的接口例如异步模型推理任务下发在stream2
aclmdlDataset *input2, *output2;
aclmdlExecuteAsync(modelId2, input2, output2, stream2);// 流同步
aclrtSynchronizeStream(stream1);
aclrtSynchronizeStream(stream2);// 释放资源
aclrtDestroyStream(stream1);
aclrtDestroyStream(stream2);
aclrtDestroyContext(context);4.3 多线程多Stream
在多线程环境下每个线程可以创建自己的Stream来执行任务。
#include acl/acl.hvoid runThread(aclrtStream stream) {int32_t deviceId 0;aclrtContext context;// 创建ContextaclrtCreateContext(context, deviceId);// 显式创建一个StreamaclrtStream threadStream;aclrtCreateStream(threadStream);// 释放资源aclrtDestroyStream(threadStream);aclrtDestroyContext(context);
}// 创建2个线程每个线程对应一个Stream
aclrtStream stream1, stream2;
std::thread t1(runThread, stream1);
std::thread t2(runThread, stream2);// 显式调用join函数确保结束线程
t1.join();
t2.join();注意事项
在创建Stream之前确保已经创建了Context。在多Stream场景下注意使用aclrtSynchronizeStream接口来同步任务的执行。在多线程环境下确保每个线程正确地创建和销毁自己的Stream和Context。在程序结束前确保释放所有Stream和Context资源避免内存泄漏。
5. 多Device切换
在AscendCL应用开发中多Device切换是一个重要的特性它允许开发者在多个昇腾AI处理器Device之间高效地切换和管理任务。
下图为同步等待流程_多Device场景 5.1 多Device切换关键接口 aclrtSetCurrentContext用于切换当前线程的Context比使用aclrtSetDevice接口效率更高。 aclrtSynchronizeDevice用于等待特定Device上的所有计算任务结束。
5.2 多Device切换流程 初始化AscendCL在使用AscendCL进行任何操作之前需要先初始化AscendCL。 aclError ret aclInit(NULL);
if (ret ! ACL_ERROR_NONE) {// 错误处理
}创建Context在多Device环境中每个Device都有一个Context。需要为每个Device创建一个Context。 aclrtContext context[DEVICE_NUM];
for (int i 0; i DEVICE_NUM; i) {ret aclrtCreateContext(context[i], i);if (ret ! ACL_ERROR_NONE) {// 错误处理}
}切换Context和Device在执行任务时使用aclrtSetCurrentContext接口切换到相应的Context从而在对应的Device上执行任务。 // 假设我们要在Device 1上执行任务
aclrtSetCurrentContext(context[1]);
// 执行任务...等待Device任务完成在需要等待特定Device上的任务完成时使用aclrtSynchronizeDevice接口。 // 等待Device 2上的任务完成
aclrtSynchronizeDevice(2);执行任务在每个Device上执行相应的任务如模型推理或算子执行。 // 模型推理示例
aclmdlExecuteAsync(modelId, input, output, stream);释放资源在任务完成后释放Context和销毁所有资源。 for (int i 0; i DEVICE_NUM; i) {aclrtDestroyContext(context[i]);
}
aclFinalize();注意事项
在多Device环境中确保为每个Device创建了对应的Context。使用aclrtSetCurrentContext接口切换Context时确保当前线程没有其他Device的任务在执行。在等待Device任务完成时确保没有其他任务依赖于这些任务的结果否则可能会导致死锁。在程序结束前确保释放所有Context资源并调用aclFinalize进行AscendCL的清理。
6. 同步等待
在AscendCL应用开发中同步等待是一个关键的概念它确保了在异步计算场景下任务的正确执行顺序和资源的正确管理。
AscendCL提供了以下同步机制
Event的同步等待使用aclrtSynchronizeEvent接口阻塞应用程序运行等待Event完成。Stream内任务的同步等待使用aclrtSynchronizeStream接口阻塞应用程序运行直到指定Stream中的所有任务都完成。Stream间任务的同步等待使用aclrtStreamWaitEvent接口阻塞指定Stream的运行直到指定的Event完成。Device的同步等待使用aclrtSynchronizeDevice接口阻塞应用程序运行直到正在运算中的Device完成运算。
6.1 Event的同步等待
#include acl/acl.h// 创建一个Event
aclrtEvent event;
aclrtCreateEvent(event);// 显式创建一个Stream
aclrtStream stream;
aclrtCreateStream(stream);// 在stream末尾添加了一个event
aclrtRecordEvent(event, stream);// 阻塞应用程序运行等待event发生也就是stream执行完成
aclrtSynchronizeEvent(event);// 显式销毁资源
aclrtDestroyStream(stream);
aclrtDestroyEvent(event);6.2 Stream内任务的同步等待
#include acl/acl.h// 显式创建一个Stream
aclrtStream stream;
aclrtCreateStream(stream);// 调用触发任务的接口传入stream参数
aclrtMemcpyAsync(dstPtr, dstSize, srcPtr, srcSize, ACL_MEMCPY_HOST_TO_DEVICE, stream);// 调用aclrtSynchronizeStream接口阻塞应用程序运行直到指定Stream中的所有任务都完成
aclrtSynchronizeStream(stream);// Stream使用结束后显式销毁Stream
aclrtDestroyStream(stream);6.3 Stream间任务的同步等待
#include acl/acl.h// 创建一个Event
aclrtEvent event;
aclrtCreateEvent(event);// 创建stream1
aclrtStream s1;
aclrtCreateStream(s1);// 创建stream2
aclrtStream s2;
aclrtCreateStream(s2);// 在s1末尾添加了一个event
aclrtRecordEvent(event, s1);// 阻塞s2运行直到指定event发生也就是s1执行完成
aclrtStreamWaitEvent(s2, event);// 显式销毁资源
aclrtDestroyStream(s2);
aclrtDestroyStream(s1);
aclrtDestroyEvent(event);6.4 Device的同步等待
#include acl/acl.h// 指定device
aclrtSetDevice(0);// 创建context
aclrtContext ctx;
aclrtCreateContext(ctx, 0);// 创建stream
aclrtStream stream;
aclrtCreateStream(stream);// 阻塞应用程序运行直到正在运算中的Device完成运算
aclrtSynchronizeDevice();// 资源销毁
aclrtDestroyStream(stream);
aclrtDestroyContext(ctx);
aclrtResetDevice(0);注意事项
在使用同步等待机制时确保正确地创建和销毁Event和Stream资源。在多Stream环境中使用Event和aclrtStreamWaitEvent接口来实现Stream间的同步等待。在多Device环境中使用aclrtSynchronizeDevice接口来等待特定Device上的任务完成。在程序结束前确保释放所有资源避免内存泄漏。。