运行时(Runtime)#

运行时 visionflow::Runtime 是检测流程 FLow 中若干算子 Operator 执行的句柄。 而运行时策略 visionflow::runtime::IStrategy 是算子如何执行的配置, 尤其是用户没有明确指定,但对于能正确执行又必须的配置。

通过运行时和运行时策略,你可以在不清楚检测流程 FLow 中算子细节和样本 Sample 属性 Property 细节的情况下推理样本。

创建运行时#

例如,你有一个已经训练好的工程 visionflow::Project my_project, 通过预设的运行时策略,那么你可以方便创建对应的运行时。

// 预设的运行时策略,下面会详细介绍。
visionflow::runtime::AllTools strategy;
// 大部分情况下这么配置即可。
strategy.options.ignore_update_time_requirement = true;
strategy.options.allow_auto_update = true;
strategy.options.allow_unrealized_oper = true;

auto runtime = my_project->create_runtime(strategy);


Note

运行时也可以在正确参数配置和完善样本数据下,自动依次完成训练和推理两部分,并不强制要求是已训练的工程。

创建运行时的策略#

VisionFlow 提供了若干预设策略方便用户使用,它们共同使用 visionflow::runtime::StrategyOptions 来作为策略的共同部分:

  1. visionflow::runtime::StrategyOptions::ignore_update_time_requirement 是否忽略参数更新时间。

  2. visionflow::runtime::StrategyOptions::allow_auto_update 是否允许参数自动更新。

    • 对于算子 Operator 而言,依赖的参数 Parameter 节点可能为空,或者因为依赖链上的节点更新而导致该节点失效。

    • 允许则会在运行时对失效参数节点自动更新节点值。

    • 否则会在创建运行时抛出异常 visionflow::excepts::NodeNotFullUpdated

  3. visionflow::runtime::StrategyOptions::allow_auto_update_rely_on_prop 是否允许依赖属性的参数自动更新。

  4. visionflow::runtime::StrategyOptions::allow_unrealized_oper 是否允许执行虚拟算子。

  5. visionflow::runtime::StrategyOptions::allow_max_gpu_num 允许使用的最大GPU数目。

  6. visionflow::runtime::StrategyOptions::specified_gpu_ids 指定优先使用的GPU编号。

  7. visionflow::runtime::StrategyOptions::allow_run_on_cpu 是否允许在CPU上运行。

  8. visionflow::runtime::StrategyOptions::call_back 回调函数。

Note

虚拟算子,即是未实现,未注册的算子 OperatorVisionFlow 中有部分属性 Property 依赖用户在使用时给出,例如图片和视窗等信息。 输出这类属性的算子即为虚拟算子。 VisionFlow 允许用户通过注册虚拟算子来在运行时自动输出对应属性,或者用户保证在创建运行时前给出该属性值即可。

具体到每种策略,描述了运行时的执行算子的范围:

  1. visionflow::runtime::AllTools 执行所有工具。

    • 自动执行工程内所有工具的输出及其依赖链上的所有算子。

    • 适用于大部分情况。

  2. visionflow::runtime::SingleTool 执行单个工具。

    • 自动执行指定工具输出及其依赖链上的工具内算子。

    • 依赖链限定在指定工具内,因此不能自动处理对于工具与工具之间的输入输出依赖关系。

    • 对于缺少输入的工具,会在运行时抛出异常 visionflow::excepts::DataNotFound

  3. visionflow::runtime::ToolsAndDepends 执行指定工具集及其依赖工具。

    • 自动执行指定工具集输出及其依赖链上的所有算子。

  4. visionflow::runtime::SingleNode 执行指定算子。

Warning

对于以工具为范围的策略而言,所需执行的算子为工具输出及其依赖链上的所有算子。 但如果某些算子并不在工具输出的依赖链上(例如只依赖某工具输出,却又不在其他工具输出的依赖链上),则不会自动执行。 这时候需要通过 visionflow::runtime::SingleNode 来单独执行。

Note

目前 VisionFlow 只支持上述预设策略,并不允许用户自定义实现注册运行时策略。

获取运行时信息#

成功创建运行时后, visionflow::Runtime 还提供了方便获取相关信息的接口:

  1. visionflow::Runtime::used_device() 允许使用的GPU编号。

  2. visionflow::Runtime::input_properties() 运行时需要的输入属性,需要用户给出。

  3. visionflow::Runtime::virtual_output_properties() 虚拟算子 输出的属性,需要用户给出。

  4. visionflow::Runtime::final_output_properties() 运行时最终输出的属性,不包括中间结果。

  5. visionflow::Runtime::all_output_properties() 运行时输出的所有属性,包括中间结果。

  6. visionflow::Runtime::required_properties() 运行时所需的所有属性, 为 visionflow::Runtime::input_properties()visionflow::Runtime::all_output_properties() 的并集。

  7. visionflow::Runtime::register_observer() 方便用户观察运行时。

  8. visionflow::Runtime::create_sample() 创建出一个满足运行时所需的样本 Sample

  9. visionflow::Runtime::is_meeting_requirement() 判断样本是否满足运行时。

    • 满足运行时即样本拥有所需的所有属性,并且需要用户给出的属性都已有数据。

通过运行时信息,可以方便创建一个满足运行时要求的样本:

// 创建满足运行时的样本,它需要用户输入图片。
auto sample = runtime.create_sample();
// 从外部读取图片。
auto image = visionflow::Image::FromFile("D:/path/to/image.png");
// 在样本中输入属性。
sample.get_or_create<visionflow::props::Image>({"Input", "image"})
    ->set_image(image);
// 判断样本是否满足运行时。
if (runtime.is_meeting_requirement(sample))
    runtime.execute(sample);


使用运行时进行推理#

对于样本集 SampleSet 而言,其中的样本可能并不同时满足同一个运行时, VisionFlow 提供了 visionflow::RuntimeSampleSetAdapter 来帮助用户自动从样本集中过滤不符合运行时要求的样本。 对于符合要求的样本,只读取运行时所需要的属性。

// 这里以工程主数据集为例,推理自然也可以使用别的数据集。
auto dataset_name = my_project->main_sample_set_name();
auto sample_set = my_project->get_sample_set(dataset_name);

// 帮助用户直接适配数据集和运行时。
auto adapter = adapt(&sample_set, runtime);

// 简单遍历执行后将结果存回数据集即可。
for (auto [id, sample] : adapter) {
    runtime.execute(sample);
    adapter.update(id, sample);
}


异步推理和多线程调用#

Runtime支持两种不同的推理模式:同步模式和异步模式。对于具有多个分支的流程,使用异步模式推理能够通过分支并行提高推理速度。 对于仅有单个分支的处理流程而言,异步模式不能直接取得加速效果,并可能由于线程同步等问题发生轻微的速度退化。 异步模式和同步模式的切换方法如下(我们已经将异步推理设置为默认的推理模式,更多细节请查看 visionflow::Runtime::execute() 的接口说明。):

// Set the second argument to true to inferring the sample in asynchronous mode.
// Or set to false to inferring in synchronous mode
runtime.execute(sample /*, true*/);
# Set the second argument to True to inferring the sample in asynchronous mode.
# Or set to False to inferring in synchronous mode
runtime.execute(sample, True)
// Set the second argument to true to inferring the sample in asynchronous mode.
// Or set to false to inferring in synchronous mode
runtime.execute(sample /*, true*/);

在异步模式下,你可以用多个线程同时调用同一个Runtime推理多个不同的样本:

void infer_task(vflow::Runtime* runtime, int camera_id){

   while(true) {
      vflow::Sample sample = runtime->create_sample();

      // get image from camera_id and put into sample

      runtime->execute(sample, true);
   }
}

void task_dispatch(vflow::Runtime* runtime) {
   std::thread th_1(infer_task, runtime, 1);
   std::thread th_2(infer_task, runtime, 2);

   th_1.join();
   th_2.join();
}


Note

多个线程同时调用同一个Runtime推理不同的样本时,由于资源竞争和样本之间的乱序执行,对于单一样本的推理时间可能会产生波动。 但一般情况下,系统的整体吞吐量可以提升(实际情况取决于硬件配置)。

Warning

同步模式下不建议使用多个不同的线程同时调用同一Runtime,尤其是 禁止不断创建不同的线程通过同步模式调用同一Runtime , 否则可能发生资源泄露等影响系统稳定运行的问题。