在默认编译模式下,main
应用作为独立控制线程来编译,此线程需在 PS 上与 AI 引擎阵列上执行的计算图并行执行。main
应用可以使用更新和读取 API 来访问任意级别的计算图内声明的运行时参数。本节使用示例描述了这些 API。
同步更新/读取
以下代码显示了 simple_param
计算图的 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;
}
在此示例中,计算图 mygraph
首先进行初始化,然后运行两次迭代。它包含一个已触发的输入参数端口 select_value
,对于接收内核的每次迭代,此端口必须以新的值来更新。update
API 的第一个实参可识别要更新的端口,第二个实参则可提供值。根据端口方向、其数据类型以及它属于标量参数还是阵列参数,可支持多种其他形式的更新 API。
如果程序编译时,测试迭代次数是固定的,那么对于已触发的参数,main
程序中更新 API 调用次数必须与测试迭代次数相匹配,否则仿真可能持续等待其他更新。对于异步参数,更新与计算图执行异步执行,如果未完成更新,那么内核会使用旧的值。
此外,如果先前的计算图编译时使用了同步 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;
}
在此示例中,假定每次迭代时,计算图都会通过 inout 端口 result_out
来生成标量结果。read
API 用于在每次迭代之后同步读取并输出此端口的值。read
API 的第一个实参是要读回的计算图 inout 端口,第二个实参则是将用于存储该值的位置(按引用传递)。
同步协议可确保读取操作将等待计算图生成值之后再对其进行采样,计算图将等待读取值后再继续下一次迭代。因此 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 次迭代。如果计算图包含异步 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;
}
在此示例中,此计算图设为永久运行。但在调用 run
API 后,它仍会阻止执行首次更新以进行参数初始化。随后,它会运行 10,000 个周期(近似),然后才会允许控制线程执行另一次更新。新更新在下一次内核调用边界后生效。随后,允许计算图再运行 15,000 个周期,而后终止。