下载
EN
注册

入门示例

目标

本节以一个简单的Add算子为例,带您快速熟悉TIK算子的编写流程。

本样例中,定义Add算子的两个输入的shape都为(128,),数据类型为float16,运行目标为昇腾AI处理器AI Core

算子分析

算子分析阶段,需要明确算子的数学表达式,输入/输出,调度流程设计,算子的实现函数名称等。

  1. Add算子的数学表达式为:
     y=x1+x2

    计算过程是:将两个输入参数相加,得到最终结果y并将其返回。

  2. 明确输入和输出。
    • Add算子有两个输入一个输出。
    • 本样例中算子的输入支持的数据类型为float16,算子输出的数据类型与输入数据类型相同。
    • 本样例中,算子输入支持的shape为(128,),输出shape与输入shape相同。
    • 算子输入支持的format为:NCHW,NC1HWC0,NHWC,ND。

      TIK算子开发方式对format不敏感,算子实现过程中不需要过多关注format。

  3. Add算子的Schedule流程设计。
    1. 定义数据,分别在AI Core外部存储与内部存储中定义数据。
    2. 数据搬运,将外部存储中的数据搬运到AI Core内部存储的Unified Buffer中。由于输入数据的shape为(128,),数据类型为float16,所以128*2=256Byte,远小于Unified Buffer的大小(例如Ascend310,Unified Buffer为256KB),所以执行一次搬运即可。
    3. 执行计算。

      调用双目矢量计算接口vec_add执行加法计算。

      由于Vector Unit一次最多可计算256Byte的数据,此样例数据为256Byte,所以让所有的Vector计算单元参与计算,执行一次计算即可。

  4. 明确算子实现文件名称、算子实现函数名称以及算子的类型(OpType)。
    • 算子类型需要采用大驼峰的命名方式,即采用大写字符区分不同的语义。
    • 算子实现文件名称和算子定义函数名称,可选用以下任意一种命名规则:
      • 用户自定义,此时需要在TBE算子信息库中配置opFile.valueopInterface.value
      • 不配置TBE算子信息库中的opFile.valueopInterface.value,FE会将OpType按照如下方式进行转换后进行算子文件名和算子函数名的匹配。
        转换规则如下:
        • 首字符的大写字符转换为小写字符。

          例如:Abc -> abc

        • 小写字符后的大写字符转换为下划线+小写字符。

          例如:AbcDef -> abc_def

        • 紧跟数字以及大写字符后的大写字符,作为同一语义字符串,查找此字符串后的第一个小写字符,并将此小写字符的前一个大写字符转换为下划线+小写字符,其余大写字符转换为小写字符。若此字符串后不存在小写字符,则直接将此字符串中的大写字符转换为小写字符。

          例如:ABCDef -> abc_def;Abc2DEf -> abc2d_ef;Abc2DEF -> abc2def;ABC2dEF -> abc2d_ef。

    因此本例中,算子类型定义为AddTik;算子的实现文件名称及实现函数名称定义为add_tik。

    通过以上分析,得到Add算子的设计规格如下:

    表1 Add算子设计规格

    算子类型(OpType)

    AddTik

    算子输入

    name:x1

    shape:

    (128,)

    data type:

    float16

    format:

    NCHW,NC1HWC0,

    NHWC,ND

    name:x2

    shape:

    (128,)

    data type:

    float16

    format:

    NCHW,NC1HWC0,

    NHWC,ND

    算子输出

    name:y

    shape:

    (128,)

    data type:

    float16

    format:

    NCHW,NC1HWC0,

    NHWC,ND

    TIK计算接口

    vec_add

    实现函数名称

    add_tik

算子代码实现

下面以此AddTik算子为例描述TIK程序的编写步骤:

  1. Python模块导入。
    from tbe import tik
    import tbe.common.platform as tbe_platform
    import numpy as np

    “tbe.tik”:提供了所有TIK相关的python函数,具体请参考CANN软件安装后文件存储路径的“python/site-packages/tbe/tik”。

  2. 定义算子实现函数。
    def add_tik():

    说明:本样例为了让大家对TIK有初步的认识,数据类型与形状都设置成了固定值,实际进行算子开发时,算子的数据是执行时才传入的,所以需要在算子实现函数定义时包含输入与输出数据基本信息,如TIK入口函数

  3. 设置昇腾AI处理器的版本,并指定运行目标。
        # soc_version请设置为实际昇腾AI处理器的型号
        tbe_platform.set_current_compile_soc_info(soc_version)

    “set_current_compile_soc_info”接口中的参数“core_type”用来指定核类型,默认值为“AiCore”,代表运行目标为AI Core

  4. 构建TIK DSL容器。
        tik_instance = tik.Tik(disable_debug=False)

    因为此样例后续要进行功能调试,所以设置“disable_debug=False”使能调试开关。

  5. 向TIK DSL容器中,插入TIK DSL语句。
    1. 在AI Core的外部存储和内部存储中定义输入数据、输出数据。
          data_A = tik_instance.Tensor("float16", (128,), name="data_A", scope=tik.scope_gm)
          data_B = tik_instance.Tensor("float16", (128,), name="data_B", scope=tik.scope_gm)
          data_C = tik_instance.Tensor("float16", (128,), name="data_C", scope=tik.scope_gm)
          data_A_ub = tik_instance.Tensor("float16", (128,), name="data_A_ub", scope=tik.scope_ubuf)
          data_B_ub = tik_instance.Tensor("float16", (128,), name="data_B_ub", scope=tik.scope_ubuf)
          data_C_ub = tik_instance.Tensor("float16", (128,), name="data_C_ub", scope=tik.scope_ubuf)
    2. 将外部存储中的数据搬入AI Core内部存储(比如Unified Buffer)中。
          tik_instance.data_move(data_A_ub, data_A, 0, 1, 128*2 //32, 0, 0)
          tik_instance.data_move(data_B_ub, data_B, 0, 1, 128*2 //32, 0, 0)
    3. 进行加法计算。
          tik_instance.vec_add(128, data_C_ub[0], data_A_ub[0], data_B_ub[0], 1, 8, 8, 8)
    4. 将数据从AI Core内部存储中搬出到AI Core外部存储。
          tik_instance.data_move(data_C, data_C_ub, 0, 1, 128*2 //32, 0, 0)
  6. 将TIK DSL容器中的语句,编译成昇腾AI处理器可执行的代码,即算子的.o文件和算子描述.json文件。
        tik_instance.BuildCCE(kernel_name="simple_add",inputs=[data_A,data_B],outputs=[data_C])

    其中,

    • kernel_name:指明编译产生的二进制代码中的核函数名称。
    • inputs:存放程序的输入Tensor,为从外部存储中加载的数据,必须是Global Memory的存储类型。
    • outputs:存放程序的输出Tensor,对应计算后搬运到外部存储中的数据,必须是Global Memory的存储类型。
  7. 返回TIK实例。
        return tik_instance

功能调试

  1. 在算子实现文件下方添加功能调试代码,用于对算子实现进行验证。
    if __name__ == "__main__":
        # 调用TIK算子实现函数
        tik_instance = add_tik()
        # 初始化数据,为128个float16类型的数字1的一维矩阵
        data = np.ones((128,), dtype=np.float16)
        feed_dict = {"data_A": data, "data_B": data}
        # 启动功能调试
        data_C, = tik_instance.tikdb.start_debug(feed_dict=feed_dict, interactive=True)
        # 打印输出数据
        print(data_C)
  2. 运行TIK Python程序。

    python3 add_tik.py

    输入data_A和data_B分别为128个float16类型的数字1的一维矩阵,展示的输出data_C如下所示:

    [TIK]>c
    [2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
     2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
     2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
     2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
     2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.
     2. 2. 2. 2. 2. 2. 2. 2.]

    以上仅对大致编程过程进行展示,后续会对上述实例中各接口、各参数的含义进行详细描述。