---
title: 单进程多卡异步运行
description: "本章节介绍如何使用单进程管理多卡，即一个进程可以并发执行在不同的Device上。"
url: https://www.hiascend.com/document/detail/zh/canncommercial/latest/programug/graphdevg/atlasag_25_0075.html
sourcePath: /source/zh/canncommercial/900/programug/graphdevg/atlasag_25_0075.html
indexId: cb6f5e5946954eefdc6ccab246d42056fc04809c5bba919feb1e34e925f3c1fe70
---
# 单进程多卡异步运行

本章节介绍如何使用单进程管理多卡，即一个进程可以并发执行在不同的Device上。

#### 功能介绍

Atlas 200I/500 A2 推理产品 不支持该特性。

涉及的主要接口为：


1. 调用  GEInitializeV2(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0176.html)
进行系统初始化（也可在Graph构建前调用），申请系统资源。
2. 调用“aclInit(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0022.html)”接口，初始化acl。
3. 调用  Session构造函数(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0282.html)
创建多个Session类对象，申请Session资源，每个Session传入不同的ge.session_device_id，将模型运行在不同的Device。
4. 创建多个线程，每个线程传入不同的Session，下面以一个线程为例，描述简单的流程：

  a. 调用“aclrtSetDevice(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0039.html)”指定运行的Device，调用“aclrtCreateStream(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0066.html)”创建Stream，然后调用“aclrtMalloc(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0095.html)”申请Device内存。
  b. 调用    AddGraph(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0170.html)
在Session类对象中添加定义好的图。
  c. 调用    CompileGraph(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0172.html)
完成图编译。
  d. 调用    LoadGraph(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0181.html)
（异步执行Graph场景），将图模型加载到    4.a
创建的Stream上。
  e. 调用“aclrtMemcpy(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0105.html)”将数据从Host传输到Device。
  f. 调用    RunGraphWithStreamAsync(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0187.html)
异步执行接口，运行Graph。
  g. 调用“aclrtSynchronizeStream(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0076.html)”阻塞程序运行，直到指定Stream中的所有任务都完成。
  h. 调用“aclrtMemcpy(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0105.html)”将数据从Device回传到Host。
  i. 调用“aclrtFree(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0100.html)”释放内存。
5. 调用  GEFinalizeV2(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0173.html)
，释放系统资源；调用“aclFinalize(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/runtimeapi/aclcppdevg_03_0023.html)”释放相关资源。


#### 开发示例

1. 包含的头文件，包括acl、C或C++标准库的头文件。
  1 2 3 4 5 #include "ge_api_v2.h" #include "acl.h" #include "acl_rt.h" #include "graph/ascend_string.h" #include <thread>

2. 申请系统资源。
  Graph定义完成后，调用GEInitializeV2进行系统初始化（也可在Graph定义前调用），申请系统资源。示例代码如下：

  1 2 3 std::map<AscendString, AscendString>config = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; Status ret = ge::GEInitializeV2(config);

  可以通过config配置传入GE运行的初始化信息，配置参数ge.exec.deviceId和ge.graphRunMode，分别用于指定GE实例运行设备，图执行模式（在线推理请配置为0，训练请配置为1）。更多配置请参考options参数说明(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0150.html)。

  GE options中的dump信息，与后续调用acl初始化接口时配置的dump信息，建议两者不要同时配置，否则可能导致异常。其他相同功能的参数类似。

3. acl资源初始化。
  1 2 3 4 5 6 7 std::string aclConfigPath = "xx/xx/xx"; aclError retInit = aclInit(aclConfigPath); if (retInit != ACL_ERROR_NONE) { // ... // ... return FAILED; }

4. 创建多个Session。
  若想使定义好的Graph运行起来，首先，要创建一个Session对象。Session中的options可以加载配置参数，支持的配置参数请参见options参数说明(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0150.html)。 1 2 3 4 5 6 7 8 9 10 11 12 13 int thread_num = 8; // 以8个device为例 for (int i= 0; i < thread_num; ++i) { // 创建多个Session，每个Session的options中，传入不同的ge.session_device_id std::map<ge::AscendString, ge::AscendString> options = { // 构造Session的配置 {"ge.session_device_id",std::to_string(i).c_str()}, }; ge::GeSession *session = new ge::GeSession(options); // 创建Session，传入配置map if (session == nullptr) { // 检查Session是否创建成功 std::cout << "create session failed!" << std::endl; ge::GEFinalizeV2(); return FAILED; } sessions.push_back(session); }

5. 创建多个线程，每个线程传入不同的Session和ge.session_device_id，进行异步运行Graph。
```
//  用来保存所有线程对象的容器    
std::vector<std::thread> threads;
// 创建多条线程并存入容器
for (int i= 0; i < thread_num; i++) {
    std::thread worker_thread(exec_func, i); // 创建一个线程，执行exec_func，exec_func线程函数如5.a~5.g步骤所示
    threads.emplace_back(std::move(worker_thread)); //通过std::move将worker_thread移动到threads容器中
}
// 等待所有线程完成
for (int i = 0; i < thread_num; i++) {
    threads.at(i).join();
}
```

单个线程exec_func异步运行步骤如下：

  a. 指定运行的Device，创建Stream，申请内存。
    1 2 3 4 5 6 7 8 9 10 11 // 指定用于运算的Device int32_t deviceId = 0; retInit = aclrtSetDevice(deviceId); // 创建一个Stream aclrtStream stream = nullptr; aclError aclRet = aclrtCreateStreamWithConfig(&stream, 0, ACL_STREAM_FAST_LAUNCH); // 申请Device上的内存 void* devPtrB = NULL; aclRet = aclrtMalloc(&devPtrB, data_size, ACL_MEM_MALLOC_HUGE_FIRST);

  b. 添加Graph对象。
    1 2 3 4 5 6 7 8 9 10 11 12 uint32_t graph_id = 0; ge::Graph graph; sess_ = sessionList[index]; ge::Status ret = sess_ -> AddGraph(graph_id, graph, graph_options); if(ret != SUCCESS) { // ... // ... // 释放资源 ge::GEFinalizeV2(); delete session; return FAILED; }

    用户可以通过传入options配置图运行相关配置信息，其中图运行完之后的数据保存在Tensor output_cov中。

  c. （可选）编译Graph。
    如果不调用CompileGraph接口，LoadGraph接口将自动调用CompileGraph以完成编译。 1 2 3 4 5 6 7 8 9 10 uint32_t graph_id = 0; ret = sess_ -> CompileGraph(graph_id); if(ret != SUCCESS) { // ... // ... // 释放资源并销毁Session ge::GEFinalizeV2(); delete session; return FAILED; }

  d. （可选）加载Graph到创建的Stream上。
    如果不调用LoadGraph接口，RunGraphWithStreamAsync接口将自动调用LoadGraph以完成加载。LoadGraph中的options可以加载配置参数，支持的配置参数请参见options参数说明(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/ascendgraphapi/atlasgeapi_07_0150.html)。 1 2 3 4 5 6 7 8 9 10 11 std::map <AscendString, AscendString> options; uint32_t graph_id = 0; ret = sess_ -> LoadGraph(graph_id, options, stream); if(ret != SUCCESS) { // ... // ... // 释放资源并销毁Session ge::GEFinalizeV2(); delete session; return FAILED; }

  e. 数据传输。
    1 2 3 // 内存复制，将Host上数据传输到Device // hostPtrA表示Host上源内存地址指针，devPtrB表示Device上目的内存地址指针，size表示内存大小 aclrtMemcpy(devPtrB, size, hostPtrA, size, ACL_MEMCPY_HOST_TO_DEVICE);

  f. 异步运行Graph，输出执行结果。
    1 2 3 4 5 6 7 8 9 std::vector<gert::Tensor> input; std::vector<gert::Tensor> output; ret = sess_->RunGraphWithStreamAsync(graph_id, stream, input, output); // 调用aclrtSynchronizeStream接口，阻塞应用程序运行，直到指定Stream中的所有任务都完成 aclRet = aclrtSynchronizeStream(stream); // 内存复制，将Device数据传回Host // devPtrA表示Device上源内存地址指针，hostPtrB表示Host上目的内存地址指针，size表示内存大小 aclrtMemcpy(hostPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_HOST);

  g. 释放内存。
    1 2 // 释放内存 ret = aclrtFree(devPtrB);

6. 释放资源。
  1 2 3 4 5 6 7 8 9 10 // 释放各个Session资源 for (auto session : sessions) { delete session; } // 释放Graph资源 ret = ge::GEFinalizeV2(); // acl去初始化 ret = aclFinalize();
