参数推断
如果在内核函数的形参中出现整数标量值,那么该参数就会变为运行时参数。在以下示例中,实参 select
和 result_out
均为运行时参数。
#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
void simple_param(input_window<int32> *in, output_window<int32> *out, int32 select, int32 &result_out);
#endif
int8
、int16
、int32
、int64
、uint8
、uint16
、uint32
、uint64
、cint16
、cint32
、float
和 cfloat
。filter_with_array_param
函数内。#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
void filter_with_array_param(input_window_cint16 * in, output_window_cint16 * out, const int32 (&coefficients)[32]);
#endif
对于函数实参内的每个参数(包括阵列参数)都会推断隐式端口。下表描述了为每个函数实参推断的端口类型。
形参 | 端口类 |
---|---|
T | Input |
const T | Input |
T & | Inout |
const T & | Input |
const T (&)[ …] | Input |
T(&)[…] | Inout |
在该表中可以看到,如果 AI 引擎无法对函数参数执行外部可见的更改,就会推断输入端口。如果形参按值传递,就会执行复制,因此对该副本执行的更改在外部不可见。如果参数以 const 限定符来传递,则无法写入该参数,因此将其作为输入端口来处理。
如果为 AI 引擎内核传递一个参数参考,并且该内核能够对其进行修改,那么就会推断 inout 端口,并且此端口可用于在 AI 引擎内核之间传递参数,或者允许从控制处理器读回结果。
graph::read()
进行读取。graph::update()
无法更新此 inout 端口。arg
列表中出现两次,一次作为 input(输入),另一次作为 inout(输入输出),例如,kernel_function(int32 foo_in, int32
&foo_out)
。参数挂接
input 和 inout 运行时参数端口均可连接到外包围的 graph 中对应的分层端口。这是参数公开以供运行时修改的机制。在以下 graph 中,为先前定义的 simple_param
内核创建了一个实例。此内核具有两个 input(输入)端口、一个 output(输出)端口和一个 inout(输入输出)端口。在实参列表中显示的第一个实参 in[0]
是输入窗口。第二个实参是输出窗口。第三个实参是运行时参数(不属于窗口或串流类型),并推断为输入参数 in[1]
,因为它按值传递。第四个实参是运行时参数,推断为输入输出参数 inout[0]
,因为它按引用传递。
in
、out
和 inout
各属于其各自的类别,并且全部从 graph 中的索引 0 开始。在以下 graph 定义中,对 simple_param
内核进行了例化,并将窗口连接至 in[0]
和 out[0]
(内核的输入窗口和输出窗口)。输入运行时参数连接到 graph 输入端口 select_value
,输入输出运行时参数连接到 graph 输入输出端口 result_out
。
class parameterGraph : public graph {
private:
kernel first;
public:
input_port select_value;
input_port in;
output_port out;
inout_port result_out;
parameterGraph() {
first = kernel::create(simple_param);
connect< window <32> >(in, first.in[0]);
connect< window <32> >(first.out[0], out);
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_port in;
output_port out;
arrayParameterGraph() {
first = kernel::create(filter_with_array_param);
connect< window <32> >(in, first.in[0]);
connect< window <32> >(first.out[0], out);
connect<parameter>(coeffs, first.in[1]);
}
};
输入参数同步
输入运行时参数的默认行为是触发行为。这表示在判定内核执行时机的规则中,该参数需要发挥作用。在此 graph 示例中,仅当满足下列三个条件时,才会执行内核:
- 输入数据具有 32 字节的有效窗口可用
- 有 32 字节的空窗口可供输出数据使用
- 发生写入输入参数的操作
在触发模式下,写入一次输入参数即可允许执行一次内核,并在每次执行内核调用时设置输入参数值。
还有另一种模式允许异步设置输入内核参数。要异步指定参数更新,请在连接端口时使用 async 修饰符。
connect<parameter>(param_port, async(first.in[1]));
内核端口指定为异步时,在用于内核的触发规则中,它不再发挥任何作用。写入一次参数时,在后续触发中会观察到其值。PS 随时都能为运行时参数写入新的值。在下一次和任意后续内核触发中都能观察到该值。
输入输出参数同步
输入输出运行时参数的默认行为是异步行为。这表示控制处理器或其它内核能够读回该参数,但生产者内核执行不受影响。对于来自 inout 参数的同步行为,如果其中内核保持阻塞直至在每次调用内核时都读出参数值,那么您可在将 inout 参数连接到外包围的 graph 的 inout 端口时使用 sync 修饰符,如下所示。
connect<parameter>(sync(first.inout[1]), param_port);