在默认编译模式下,main
应用作为独立控制线程来编译,此线程需在 PS 上与 AI 引擎阵列上执行的 graph 并行执行。main
应用可以使用更新和读取 API 来访问任意级别的 graph 内声明的运行时参数。本节使用示例描述了这些 API。
同步更新/读取
以下代码显示了 simple_param
graph 的 main
应用,如 执行运行时数据参数 中所述。
#include "param.h"
parameterGraph mygraph;
int main(void) {
mygraph.init();
mygraph.run(2);
mygraph.update(mygraph.select_value, 23);
mygraph.update(mygraph.select_value, 45);
mygraph.end();
return 0;
}
在此示例中,graph mygraph
首先进行初始化,然后运行两次迭代。它包含一个已触发的输入参数端口 select_value
,对于接收内核的每次迭代,此端口必须以新的值来更新。update
API 的第一个实参可识别要更新的端口,第二个实参则可提供值。根据端口方向、其数据类型以及它属于标量参数还是阵列参数,可支持多种其它形式的更新 API,欲知详情,请参阅 自适应数据流 graph 规范引用。
如果程序编译时,测试迭代次数是固定的,那么对于已触发的参数,main
程序中更新 API 调用次数必须与测试迭代次数相匹配,否则仿真可能持续等待其它更新。对于异步参数,更新与 graph 执行异步执行,如果未完成更新,那么内核会使用旧的值。
此外,如果先前的 graph 编译时使用了同步 inout(输入输出)参数,那么更新和读取调用必须交织,如以下示例所示。
#include "param.h"
parameterGraph mygraph;
int main(void) {
int result0, result1;
mygraph.init();
mygraph.run(2);
mygraph.update(mygraph.select_value, 23);
mygraph.read(mygraph.result_out, result0);
mygraph.update(mygraph.select_value, 45);
mygraph.read(mygraph.result_out, result1);
mygraph.end();
return 0;
}
在此示例中,假定每次迭代时,graph 都会通过 inout 端口 result_out
来生成标量结果。read
API 用于在每次迭代之后同步读取并输出此端口的值。read
API 的第一个实参是要读回的 graph inout 端口,第二个实参则是将用于存储该值的位置(按引用传递)。
同步协议可确保读取操作将等待 graph 生成值之后再对其进行采样,graph 将等待读取值后再继续下一次迭代。因此 update
(更新)操作与 read
(读取)操作交织就显得尤为重要。
异步更新/读取
以异步协议指定输入参数时,内核执行会等待发生首次更新后再执行参数初始化。但在下一次更新前可能会发生任意次数的内核调用。应用部署期间,这通常符合异步更新的意图。但对于部署,wait
API 可用于在下次更新前完成一组预先确定的迭代,如以下示例所示。
#include "param.h"
asyncGraph mygraph;
int main(void) {
int result0, result1;
mygraph.init();
mygraph.update(mygraph.select_value, 23);
mygraph.run(5);
mygraph.wait();
mygraph.update(mygraph.select_value, 45);
mygraph.run(15);
mygraph.end();
return 0;
}
在前述示例中,完成初始更新后,会运行 5 次迭代直至完成,随后再执行一次更新,后接另一组 15 次迭代。如果 graph 包含异步 inout 端口,那么等待(或结束)后即可立即读回该数据。
另一个异步更新模板是在 wait
API 中使用超时,如以下示例所示。
#include "param.h"
asyncGraph mygraph;
int main(void) {
int result0, result1;
mygraph.init();
mygraph.run();
mygraph.update(mygraph.select_value, 23);
mygraph.wait(10000);
mygraph.update(mygraph.select_value, 45);
mygraph.resume();
mygraph.end(15000);
return 0;
}
在此示例中,此 graph 设为永久运行。但在调用 run
API 后,它仍会阻止执行首次更新以进行参数初始化。随后,它会运行 10,000 个周期(近似),然后才会允许控制线程执行另一次更新。新更新在下一次内核调用边界后生效。随后,允许 graph 再运行 15,000 个周期,而后终止。