同步

调度机制

MediaPipe 图中的数据处理发生在定义为 CalculatorBase 子类的处理节点内部。调度系统会决定每个计算器的运行时间。

每个图至少有一个调度器队列。每个调度器队列只有一个执行器executor。节点以静态方式分配给队列(进而分配给执行器)。默认情况下,有一个队列,其执行器是一个线程池,其中包含多个线程,具体取决于系统功能。

每个节点都有一个调度状态,可以是未准备就绪就绪正在运行。就绪函数决定了节点是否已准备好运行。此函数会在图表初始化时、每当节点完成运行以及节点输入的状态发生变化时调用。

使用的就绪函数取决于节点的类型。没有流输入的节点称为“源节点”;源节点始终可以运行,直到它们告知框架没有更多数据要输出,此时它们会关闭。

如果非源节点有要处理的输入,并且这些输入根据节点的输入政策(参见下文)设置的条件形成有效的输入集,则它们准备就绪。大多数节点使用默认输入政策,但有些节点会指定其他输入政策。

当节点就绪时,系统会将一个任务添加到相应的调度器队列,该队列是一个优先级队列。优先级函数目前是固定的,它会考虑节点的静态属性及其在图中的拓扑排序。例如,更靠近图输出侧的节点具有较高的优先级,而源节点具有最低的优先级。

每个队列都由一个执行器处理,该执行器负责通过调用计算器的代码实际运行任务。可以提供和配置不同的执行器;这可用于自定义执行资源的使用,例如通过在优先级较低的线程上运行某些节点。

时间戳同步

MediaPipe 图的执行是分散的:没有全局时钟,不同节点可以同时处理来自不同时间戳的数据。这样可以通过流水线实现更高的吞吐量。

但是,时间信息对许多感知工作流非常重要。接收多个输入流的节点通常需要以某种方式协调它们。例如,对象检测器可能会输出帧的边界矩形列表,并且此信息可能会被馈送到渲染节点,该渲染节点应与原始帧一起对其进行处理。

因此,MediaPipe 框架的一项关键职责是为节点提供输入同步。在框架机制方面,时间戳的主要作用是充当同步键

此外,MediaPipe 旨在支持确定性操作,这在许多场景(测试、模拟、批处理等)中都很重要,同时允许图表作者根据需要放宽确定性以满足实时约束。

同步和确定性的两个目标奠定了多项设计选择的基础。值得注意的是,推送到给定数据流的数据包必须具有单调递增的时间戳:这不仅对许多节点来说是一个有用的假设,还取决于同步逻辑。每个数据流都有一个时间戳边界,这是数据流上允许的新数据包的最短时间戳。当到达时间戳为 T 的数据包时,绑定会自动前进到 T+1,以反映单调要求。这样,框架就可以确定不会再收到时间戳低于 T 的数据包。

输入政策

使用节点指定的输入政策在每个节点本地处理同步。

DefaultInputStreamHandler 定义的默认输入政策提供确定性的输入同步,并保证以下保证:

  • 如果在多个输入流上提供了具有相同时间戳的数据包,则无论这些数据包的实时到达顺序如何,它们始终都会一起处理。

  • 输入集会严格按时间戳顺序进行处理。

  • 系统不会丢弃任何数据包,并且处理过程具有完全确定性。

  • 鉴于上述保证,节点可以尽快处理数据。

为了说明其工作原理,我们需要引入已确定时间戳的定义。如果数据流中的时间戳低于时间戳边界,则视为该时间戳已得到解决。换言之,一旦某个时间戳处的输入状态不可撤消,系统就会确定该流的时间戳:要么存在数据包,要么确定具有该时间戳的数据包不会到达。

如果某个时间戳被确定为每个数据流,则该时间戳将跨多个数据流确定。此外,如果某个时间戳已结算,则意味着之前的所有时间戳也已结算。因此,可以确定性地按升序处理已确定的时间戳。

根据此定义,如果有时间戳在所有输入流中确定,并且至少一个输入流上包含数据包,则采用默认输入政策的计算器已准备就绪。输入政策将固定时间戳的所有可用数据包作为计算器的单个输入集提供。

这种确定性行为的一个后果就是,对于具有多个输入流的节点,在理论上,时间戳可能会无限等待,而在此期间可以缓冲无限数量的数据包。(假设某个节点具有两个输入流,其中一个不断发送数据包,而另一个不发送任何数据包,也不会超出限制)。

因此,我们还提供自定义输入政策:例如,将输入拆分到由 SyncSetInputStreamHandler 定义的不同同步集中,或完全避免同步并在输入通过 ImmediateInputStreamHandler 定义后立即对其进行处理。

流控制

有两种主要的流控制机制。当某个数据流上缓冲的数据包达到由 CalculatorGraphConfig::max_queue_size 定义的(可配置)限制时,背压机制会限制上游节点的执行。这种机制保持了确定性的行为,并且包含一个在需要时放宽配置限制的死锁避免系统。

第二个系统由插入特殊节点组成,这些节点可以根据由 FlowLimiterCalculator 定义的实时限制(通常使用自定义输入政策)丢弃数据包。例如,一种通用模式会在子图的输入处放置一个流控制节点,并通过从最终输出到流控制节点的环回连接来连接该节点。因此,流控制节点能够跟踪下行图中正在处理的时间戳的数量,并在此计数达到(可配置)限制时丢弃数据包;由于数据包在上游被丢弃,因此我们可以避免因部分处理时间戳,然后在中间阶段之间丢弃数据包而导致的浪费工作。

这种基于计算器的方法让图表创建者能够控制可以丢弃数据包的位置,并允许根据资源限制灵活地调整和自定义图表的行为。