昇腾社区首页
中文
注册
开发者
下载

概念说明

基本概念

  • Host

    Host指与Device相连接的X86服务器、ARM服务器,会利用Device提供的NN(Neural-Network)计算能力,完成业务。

  • Device(或者NPU)

    Device指安装了昇腾AI处理器的硬件设备,利用PCIe接口与Host侧连接,提供NN计算能力。

  • Context

    Context作为一个容器,管理了所有对象(包括Stream、Event、设备内存等)的生命周期。不同Context中的对象是完全隔离的,例如不同Context的Stream、不同Context的Event是完全隔离的,无法建立同步等待关系。

    Context分两种:
  • Stream

    Stream用于维护一些异步操作的执行顺序,确保同一个Stream中的任务按照应用程序中的代码调用顺序在Device上执行。

    基于Stream的kernel执行和数据传输能够实现Host运算操作、Host与Device间的数据传输、Device内的运算并行。

    Stream分两种:
  • Event

    Event通常用于一个Device内、两个Stream之间的任务同步。例如,若stream2的任务依赖stream1的任务,想保证stream1中的任务先完成,这时可创建一个Event,将Event插入到stream1中(通常称为Event Record任务),在stream2中插入一个等待Event完成的任务(通常称为Event Wait任务)。另外,Event支持记录事件时间戳信息。

  • Notify

    Notify通常用于Device与Device之间的状态/动作通信通知。例如:Device 0向Device 1发送完数据后,通过Notify机制通知Device 1数据已写完。Notify没有记录时间戳的功能。

    Notify只支持一对一通知机制。若要实现向多个Device发起通知,要发起多次Notify操作,如下图所示:

    Notify与Event主要区别在于,Notify Wait完成后,Notify状态会自动重置,因此一个Notify Record任务只能通知一个Notify Wait任务;而Event Wait并不会自动重置Event状态,因此一个Event Record任务可以做到通知一个或多个Event Wait任务。

  • CntNotify

    CntNotify通常也用于Device与Device之间的状态/动作通信通知。

    但CntNotify是利用计数值实现任务间的同步,跟Notify的区别是,Notify的计数值仅支持1,CntNotify的计数值支持[1~uint32_t最大值]。

Device、Context、Stream之间的关系

图1 Device、Context、Stream之间的关系
  • Device,表示计算设备,用户可以调用acl接口,例如aclrtSetDevice,指定当前线程中用于运算的设备。
  • Context,在Device下,一个Context一定属于一个唯一的Device。
    • Context分隐式创建和显式创建。
    • 隐式创建的Context(即默认Context),调用aclrtSetDevice接口会隐式创建默认Context。
    • 显式创建的Context,调用aclrtCreateContext接口会显式创建Context,调用aclrtDestroyContext接口显式销毁Context。
    • 若在某一进程内创建多个Context(Context的数量与Stream相关,Stream数量有限制,请参见aclrtCreateStream),当前线程在同一时刻内只能使用其中一个Context,建议通过aclrtSetCurrentContext接口明确指定当前线程的Context,增加程序的可维护性
    • 进程内的Context是共享的,可以通过aclrtSetCurrentContext进行切换。
  • Stream,是Device上的执行流,在同一个stream中的任务执行严格保序。
    • Stream分隐式创建和显式创建。
    • 每个Context都会包含一个默认Stream,这个属于隐式创建。
    • 用户可以显式创建Stream,调用aclrtCreateStream接口显式创建Stream,调用aclrtDestroyStream接口显式销毁Stream。显式创建的Stream归属的Context被销毁后,会影响该Stream的使用,虽然此时Stream没有被销毁,但不可再用。
  • Task/Kernel,是Device上真正的任务执行体。

线程、Context、Stream之间的关系

  • 一个用户线程一定会绑定一个Context,所有Device的资源使用或调度,都必须基于Context。
  • 一个线程中当前会有一个唯一的Context在用,Context中已经关联了本线程要使用的Device。
  • 可以通过aclrtSetCurrentContext进行Device的快速切换。示例代码如下,仅供参考,不可以直接拷贝编译运行:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    // ......
    aclrtCreateContext(&ctx1, 0);
    aclrtCreateStream(&s1);
    /* 执行算子 */
    aclopExecuteV2(op1,...,s1);
    
    aclrtCreateContext(&ctx2,1);
    /* 在当前线程中,创建ctx2后,当前线程对应的Context切换为ctx2,后续计算任务在Device 1上进行 */
    aclrtCreateStream(&s2);
    /* 执行算子 */
    aclopExecuteV2(op2,...,s2);
    
    /* 在当前线程中,通过Context切换,使后续计算任务在对应的Device 0上进行 */
    aclrtSetCurrentContext(ctx1);
    /* 执行算子 */
    aclopExecuteV2(op3,...,s1);
    // ......
    
  • 一个线程中可以创建多个Stream,不同的Stream上计算任务是可以并行执行;多线程场景下,推荐每个线程创建一个Stream,线程之间的Stream在Device上相互独立,每个Stream内部的任务是按照Stream下发的顺序执行。
  • 多线程的调度依赖于运行应用的操作系统调度,多Stream在Device侧的调度,由Device上调度组件进行调度。

一个进程内多个线程间的Context切换

  • 一个进程中可以创建多个Context,但一个线程同一时刻只能使用一个Context。
  • 线程中创建的多个Context,线程缺省使用最后一次创建的Context。
  • 进程内创建的多个Context,可以通过aclrtSetCurrentContext设置当前需要使用的Context。
图2 接口调用流程

默认Context和默认Stream的使用场景

  • Device上执行操作下发前,必须有Context和Stream,这个Context、Stream可以显式创建,也可以隐式创建。隐式创建的Context、Stream就是默认Context、默认Stream。

    默认Stream作为接口入参时,直接传NULL。

  • 默认Context不允许用户执行aclrtGetCurrentContextaclrtSetCurrentContext操作,也不允许执行aclrtDestroyContext操作。
  • 默认Context、默认Stream一般适用于简单应用,用户仅需要一个Device的计算场景下。多线程应用程序建议全部使用显式创建的Context和Stream。

示例代码如下,仅供参考,不可以直接拷贝编译运行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// ......
aclInit(...);
aclrtSetDevice(0); 

/*已经创建了一个default context,在default context中创建了一个default stream,并且在当前线程可用*/
// ......
aclopExecuteV2(op1,...,NULL);  //最后一个NULL表示在default stream上执行算子op1
aclopExecuteV2(op2,...,NULL); //最后一个NULL表示在default stream上执行算子op2
aclrtSynchronizeStream(NULL); 

/*等待计算任务全部完成(op1、op2执行结束),用户根据需要获取计算任务的输出结果*/
// ......
aclrtResetDevice(0);  //释放计算设备0,对应的default context及default stream生命周期也终止。

多线程、多stream的性能说明

  • 线程调度依赖运行的操作系统,Stream上下发了任务后,Stream的调度由Device的调度单元调度,但如果一个进程内的多Stream上的任务在Device存在资源争抢的时候,性能可能会比单Stream低。
  • 当前昇腾AI处理器有不同的执行部件,如AI Core、AI CPU、Vector Core等,对应使用不同执行部件的任务,建议多Stream的创建按照算子执行引擎划分。
  • 单线程多Stream与多线程多Stream(一个进程中可以包含多个线程,每个线程中一个Stream)性能上哪个更优,具体取决于应用本身的逻辑实现,一般来说前者性能略好,原因是相对后者,应用层少了线程调度开销。