Sequential
Sequential
能串联多个后端。 也就是说,Sequential[DecodeTensor,ResizeTensor,cvtColorTensor,SyncTensor]
和 Sequential[DecodeMat,ResizeMat]
是有效后端。
在 Sequential[DecodeMat,ResizeMat]
的前向执行中,数据(dict)会依次经过下列流程:
- 执行
DecodeMat
:DecodeMat
读取data
, 并将结果赋值给result
和color
- 条件控制流:尝试将数据中的
result
的值赋值给data
并删除result
- 执行
ResizeMat
:ResizeMat
读取data
, 并将结果赋值给result
键值
Sequential
可简写为S
. API可参考Sequential
.
条件控制流 filter
Sequential
这个后端本身实现了对控制流语法扩展的支持。事实上,S[DecodeMat,ResizeMat]
等价于S[(Run)DecodeMat,(swap)ResizeMat]
.
()
括号内的filter可返回以下状态:
- Run, 代表可以跳过当前子后端,等待进入下一个子后端(从0.3.2b1开始生效;之前版本可使用Continue)
- Break, 代表跳出Sequential的执行
- Error, 代表出现错误
- SerialSkip, 代表可以连续跳过串行子后端,在
Sequential
中,等价于Break - SubGraphSkip, 代表可以连续跳过子图,在
Sequential
中,等价于Break
部分内置filter定义如下:
- Run
- swap
#include "filter.hpp"
class FilterRun : public Filter {
public:
status forward(dict data) override { return Filter::status::Run; }
};
IPIPE_REGISTER(Filter, FilterRun, "Run,run");
class Filter {
public:
enum struct status { Run, Skip, SerialSkip, SubGraphSkip, Break, Error };
virtual bool init(const std::unordered_map<std::string, std::string>& /*config*/,
dict /*dict_config*/) {
return true;
};
virtual status forward(dict input) {
auto iter = input->find(TASK_RESULT_KEY);
if (iter == input->end()) {
return status::Break;
}
(*input)[TASK_DATA_KEY] = (*input)[TASK_RESULT_KEY];
input->erase(iter);
return status::Run;
}
virtual ~Filter() = default;
};
更多请查看filter更详细的说明. 据此,类似于下一节将要介绍的扩展和编译自定义后端,用户可以方便的定义自己的条件控制流。
条件控制流在输入范围变化时的反应
假设有如下后端 S[DecodeTensor,(swap)TensorrtTensor,TensorrtSync]
,其中DecodeTensor
的输入范围是[1,1],
而TensorrtTensor
的范围刚好是[1,4]. 根据Sequential计算输入范围的规则,Sequential的输入范围是[1,4].
如果同时有四个数据进入此Sequential,但是其中一个解码失败,那么此时
- 一个数据在
swap
中返回status::Break
,其余返回status::Run
. Sequential
将三个数据继续送入下一个阶段, 而Break
状态的数据终止Sequential中的执行
以上是遇到部分数据Break
时的反应;类似的,遇到部分数据Skip
,也会类似地局部性处理数据。然而,当一个数据Error
时,Sequential会认为任务整体的状态是Error
. 如果这与使用者的本意相违背,使用者可以尝试将子后端拆分到不同的Sequential中,以避免互相影响。注意,Error
状态一般是极其稀有的,预料外的状态。