流水线实现

2024年8月28日 | 阅读 4 分钟

为了实现管道以评估给定用户查询的多个操作,我们需要构建一个单一的复杂操作,该操作合并给定查询的多个操作,从而实现管道。但是,对于某些频繁出现的情况,这种方法是可行且高效的。

系统可以采用以下任何一种方式来执行管道:

需求驱动管道

在需求驱动管道中,系统会反复从管道顶部的操作请求元组。每当操作收到系统请求元组时,它首先计算将要返回的下一个元组,然后返回请求的元组。操作每次收到系统发出的任何元组请求时都会重复相同的过程。如果操作的输入不是管道化的,那么我们仅从输入关系中计算下一个返回的元组。但是,系统会跟踪迄今为止已返回的所有元组。但如果存在一些管道化输入,该操作还会从其管道化输入请求元组。收到来自其管道化输入的元组后,该操作将使用它们来计算其输出或结果的元组,然后将其传递给其父级(位于上层)。因此,在需求驱动管道中,管道是根据系统发出的元组的需求或请求来实现的。

实现需求驱动管道

在需求驱动管道中,每个操作都实现为迭代器。迭代器提供三个基本函数来实现需求驱动管道。这些函数是 open()、next() 和 close()。这些函数的工作方式如下:

  • 调用 open() 函数后,每次调用 next() 都会将操作的下一个元组作为输出返回。
  • 反过来,操作的实现会调用其输入的 open() 和 next() 函数,以便在需要时可以轻松获取输入元组。
  • 在满足要求后,close() 函数会告知迭代器不再需要元组。
  • 此外,在调用过程或调用之间,迭代器会维护其执行状态。因此,连续的 next() 函数会收到连续结果的元组。

生产者驱动管道

生产者驱动管道与需求驱动管道不同。在生产者驱动管道中,操作不会等待系统请求来生成元组。相反,操作渴望生成这些元组。在生产者驱动管道中,它将每个操作建模为系统内的独立线程或进程。在这里,系统从其管道化输入获取元组流,并最终生成或产生用于其输出的元组流。生产者驱动管道遵循这种方法。

实现生产者驱动管道

实现生产者驱动管道的方式与需求驱动管道不同。实现过程按照以下步骤进行:

  • 对于相邻操作的每一对,系统会构建一个缓冲区,用于保存从一个操作传递到下一个操作的元组。
  • 创建缓冲区后,对应于不同操作的进程会并发执行。
  • 所有位于管道底部的操作会不断地生成输出元组并将其放入输出缓冲区,直到缓冲区满为止。
  • 一旦操作使用来自管道化输入的元组,它就会从输入缓冲区中删除该元组。
  • 如果输出缓冲区已满,该操作会等待,直到缓冲区为更多元组腾出更多空间。发生的情况是,指定操作的父操作负责从缓冲区中删除元组。所以,实际上,该操作在等待其父操作执行此操作。
  • 因此,当缓冲区再次腾出更多空间时,该操作会重新开始其元组生成,并继续进行,直到缓冲区再次满为止。
  • 该操作会重复此过程,直到生成所有输出元组。

注意:当输入缓冲区为空、输出缓冲区已满,或者需要更多输入元组来生成更多输出元组时,系统切换操作是必要的。

生产者驱动管道与需求驱动管道的区别

需求驱动管道和生产者驱动管道之间存在以下区别点:

需求驱动管道生产者驱动管道
类似于从操作树的顶部拉取数据。类似于从操作树的底部推送数据。
元组以懒惰的方式生成。元组是急切生成的。
易于实现。实现生产者驱动管道并不容易。
它最常用于评估表达式。它通常很少在系统中用于。但是,它对于并行处理系统等系统很有用。