指定运行时数据参数 - 2023.2 简体中文

AI 引擎内核与计算图编程指南 (UG1079)

Document ID
UG1079
Release Date
2023-12-04
Version
2023.2 简体中文

参数推断

如果在内核函数的形参中出现整数标量值,那么该参数就会变为运行时参数。在以下示例中,实参 selectresult_out 均为运行时参数。

#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
  void simple_param(input_buffer<int32> &in, output_buffer<int32> &out, int32 select, int32 &result_out);
#endif
运行时参数是作为端口搭配串流和缓冲器所创建的参数来一起处理的。标量和标量阵列这两种数据类型均可作为运行时参数来传递。受支持的有效标量数据类型包括:int8int16int32int64uint8uint16uint32uint64cint16cint32float
注释: 结构体和指针不得作为运行时参数来传递。每个 AI 引擎都有不同的存储器空间,指针在 AI 引擎之间传递时具有不同的含义。此外,PS 和 AI 引擎具有不同的指针表示法。由于结构体可包含指针作为其成员,因此不支持将结构体作为运行时参数来传递。
在以下示例中,将 32 个整数构成的单个阵列作为一个参数传递到 filter_with_array_param 函数内。
#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H

  void filter_with_array_param(input_buffer<cint16> & in, output_buffer<cint16> * out, const int32 (&coefficients)[32]);

#endif

对于函数实参内的每个参数(包括阵列参数)都会推断隐式端口。下表描述了为每个函数实参推断的端口类型。

表 1. 按参数划分的端口类型
形参 端口类
T 输入
const T 输入
T & 输入输出
const T & 输入
const T (&)[ …] 输入
T(&)[…] 输入输出

在该表中可以看到,如果 AI 引擎无法对函数参数执行外部可见的更改,就会推断输入端口。如果形参按值传递,就会执行复制,因此对该副本执行的更改在外部不可见。如果参数以 const 限定符来传递,则无法写入该参数,因此将其作为输入端口来处理。

如果为 AI 引擎内核传递一个参数参考,并且该内核能够对其进行修改,那么就会推断 inout 端口,并且此端口可用于允许从控制处理器读回结果。

注释: inout 端口即内核本身可读取或写入的端口。但从计算图中,inout 端口只能像输出端口一样操作。因此,该 inout 端口只能由 graph::read() 进行读取。graph::update() 无法更新此 inout 端口。
注释: 如果内核要接受输入运行时参数、修改其值并通过输出运行时参数来传回此修改后的值,那么该变量必须在 arg 列表中出现两次,一次作为 input(输入),另一次作为 inout(输入输出),例如,kernel_function(int32 foo_in, int32 &foo_out)

参数挂接

input 和 inout 运行时参数端口均可连接到外包围的计算图中对应的分层端口。这是参数公开以供运行时修改的机制。在以下计算图中,为先前定义的 simple_param 内核创建了一个实例。此内核具有两个 input 端口、一个 output 端口和一个 inout 端口。在实参列表中显示的第一个实参 in[0] 是输入缓冲器。第二个实参是输出缓冲器。第三个实参是运行时参数(不属于缓冲器或串流类型),并推断为输入参数 in[1],因为它按值传递。第四个实参是运行时参数,推断为 inout 参数 inout[0],因为它按引用传递。

注释: 内核的不同类型的端口 inoutinout 各属于其各自的类别,并且全部从计算图中的索引 0 开始。

在以下计算图定义中,对 simple_param 内核进行了例化,并将缓冲器连接至 in[0]out[0](内核的输入缓冲器和输出缓冲器)。输入运行时参数连接到计算图的 input 端口 select_valueinout 运行时参数连接到计算图的 inout 端口 result_out

class parameterGraph : public graph {
private:
  kernel first;
  
public:
  input_port select_value;
  input_plio in;
  output_plio out;
  inout_port result_out;
  parameterGraph() {
    first = kernel::create(simple_param);
    ......
    connect(in.out[0], first.in[0]);
    connect(first.out[0], out.in[0]);
    connect<parameter>(select_value, first.in[1]);//default sync rtp input
    connect<parameter>(first.inout[0], result_out);//default async rtp output
  }
};

阵列参数也能以相同方式挂接。编译器会为阵列数据自动分配空间,这样即可从用于从中映射该内核的处理器进行访问。

class arrayParameterGraph : public graph {
private:
  kernel first;
  
public:
  input_port coeffs;
  input_plio in;
  output_plio out;
  arrayParameterGraph() {
    first = kernel::create(filter_with_array_param);
    ......
    connect(in.out[0], first.in[0]);
    connect(first.out[0], out.in[0]);
    connect<parameter>(coeffs, first.in[1]);
  }
};

输入参数同步

输入运行时参数的默认行为是触发行为。这表示在判定内核执行时机的规则中,该参数需要发挥作用。在此计算图示例中,仅当满足下列三个条件时,才会执行内核:

  • 输入数据具有 32 字节的有效缓冲器可用
  • 有 32 字节的空缓冲器可供输出数据使用
  • 发生写入输入参数的操作

在触发模式下,写入一次输入参数即可允许执行一次内核,并在每次执行内核调用时设置输入参数值。

还有另一种模式允许异步设置输入内核参数。要异步指定参数更新,请在连接端口时使用 async 修饰符。

connect<parameter>(param_port, async(first.in[1]));

内核端口指定为异步时,在用于内核的触发规则中,它不再发挥任何作用。写入一次参数时,在后续触发中会观察到其值。PS 随时都能为运行时参数写入新的值。在下一次和任意后续内核触发中都能观察到该值。

输入输出参数同步

输入输出运行时参数的默认行为是异步行为。这表示控制处理器或其他内核能够读回该参数,但生产者内核执行不受影响。对于来自 inout 参数的同步行为,如果其中内核保持阻塞直至在每次调用内核时都读出参数值,那么您可在将 inout 参数连接到外包围的计算图的 inout 端口时使用 sync 修饰符,如下所示。

connect<parameter>(sync(first.inout[1]), param_port);