【问题标题】:Optimized algorithm to schedule tasks with dependency?优化算法来调度具有依赖性的任务?
【发布时间】:2013-08-21 06:38:03
【问题描述】:

有些任务从文件中读取,进行一些处理并写入文件。这些任务将根据依赖关系进行调度。任务也可以并行运行,因此需要优化算法以串行运行相关任务,并尽可能并行运行。

例如:

  1. A -> B
  2. A -> C
  3. B -> D
  4. E -> F

所以运行它的一种方法是运行 1、2 和 4 并联。其次是 3。

另一种方式可能是 运行 1,然后并行运行 2、3 和 4。

另一个可以串行运行 1 和 3,并行运行 2 和 4。

有什么想法吗?

【问题讨论】:

  • 什么是A,B,...?并行运行12 是否意味着A 运行了两次?这是一件坏事吗?
  • 我的理解是 A,B ... 是任务,而 1,2,3 是依赖声明。我会说通常 D 依赖于 B 依赖于 A,依此类推。
  • @njzk2 1,2,3 和 4 是任务。 A、B 等是文件。因此,任务 1 从 A 读取并写入 B。因此,除非任务 1 完成,否则任务 3 无法启动。
  • @user2186138 :好的,那么您需要首先将其建模为“1->3”(这是唯一的依赖项)

标签: algorithm scheduled-tasks scheduling


【解决方案1】:

让每个任务(例如A,B,...)成为directed acyclic graph 中的节点,并根据您的1,2,... 定义节点之间的弧。

然后您可以topologically order 您的图表(或使用基于搜索的方法,如BFS)。在您的示例中,C<-A->B->DE->F 因此,AE 的深度为 0,需要先运行。然后你可以并行运行F,BC,然后是D

另外,看看PERT

更新:

你怎么知道B的优先级是否高于F

这是用于查找排序的拓扑排序背后的直觉。

它首先找到根节点(没有传入边)(因为必须存在于 DAG 中)。在你的情况下,那是A & E。这解决了需要完成的第一轮工作。接下来,需要完成根节点的子节点(BCF)。这很容易通过查询您的图表来获得。然后重复该过程,直到没有找到(完成)节点(作业)。

【讨论】:

  • 这个算法怎么知道B应该比F有更高的优先级?
  • 这是由您的图搜索算法完成的。您可以通过查看弧线列表(例如1.A->B)轻松构建图表。阅读拓扑排序算法。
  • 我认为拓扑排序是不够的,因为 B 和 F 彼此没有关系,所以不能这样排序。必须添加优先系统,我认为使用给定节点的依赖数。
  • 为什么不能同时处理BF
  • 同意。拓扑排序完全是解决依赖关系的正确方法。
【解决方案2】:

您的任务是一个(希望)没有循环的有向图。

I 包含 sourceswells(源是不依赖的任务(没有入站边缘),井是不解锁任务的任务(没有出站边缘))。

一个简单的解决方案是根据任务的有用性优先考虑您的任务(我们称之为U

通常情况下,从井开始,它们会有用处 U = 1,因为我们希望它们完成。

将所有井的前辈放入当前正在评估节点的L 列表中。

然后,取L中的每个节点,U的值是依赖他的节点的U值之和+1。将当前节点的所有父节点放入L列表.

循环直到所有节点都被处理完。

然后,启动可以启动且U值最大的任务,因为它会解锁最多的任务。

在你的例子中,

U(C) = U(D) = U(F) = 1
U(B) = U(E) = 2
U(A) = 4

意思是如果可能的话,你会先用 E 开始 A,然后是 B 和 C(如果可能的话),然后是 D 和 F

【讨论】:

    【解决方案3】:

    首先生成任务的拓扑排序。在这个阶段检查周期。此后,您可以通过查看最大反链来利用并行性。粗略地说,这些是任务集,它们的元素之间没有依赖关系。

    从理论角度来看,this paper 涵盖了该主题。

    【讨论】:

      【解决方案4】:

      给定项目和它们所依赖的项目之间的映射,拓扑排序对项目进行排序,以便没有项目位于它所依赖的项目之前。

      This Rosetta code task 有一个solution in Python,它可以告诉您哪些项目可以并行处理。

      根据您的输入,代码变为:

      try:
          from functools import reduce
      except:
          pass
      
      data = { # From: http://stackoverflow.com/questions/18314250/optimized-algorithm-to-schedule-tasks-with-dependency
          # This   <-   This  (Reverse of how shown in question)
          'B':         set(['A']),
          'C':         set(['A']),
          'D':         set(['B']),
          'F':         set(['E']),
          }
      
      def toposort2(data):
          for k, v in data.items():
              v.discard(k) # Ignore self dependencies
          extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys())
          data.update({item:set() for item in extra_items_in_deps})
          while True:
              ordered = set(item for item,dep in data.items() if not dep)
              if not ordered:
                  break
              yield ' '.join(sorted(ordered))
              data = {item: (dep - ordered) for item,dep in data.items()
                      if item not in ordered}
          assert not data, "A cyclic dependency exists amongst %r" % data
      
      print ('\n'.join( toposort2(data) ))
      

      然后生成此输出:

      A E
      B C F
      D
      

      输出的一行中的项目可以按任何子顺序进行处理,或者实际上是并行处理;只要较高行的所有项目都在后续行的项目之前处理以保留依赖关系。

      【讨论】:

      • A、B、C 等只是文件名。任务编号为 1-4。所以任务 1 从 A 读取并加载文件 B 等等。
      • 所以基本上我将不得不承担这些任务,建立它们之间的依赖关系,如 3 --> 1 然后使用拓扑排序。
      • 是的。这是正确的。这会导致问题@user2186138 吗? (P.S. 你有很大一部分问题没有接受任何答案)。
      【解决方案5】:

      不考虑问题的串行/并行方面,这段代码至少可以确定整体串行解决方案:

      def order_tasks(num_tasks, task_pair_list):
          task_deps= []
          #initialize the list
          for i in range(0, num_tasks):
              task_deps[i] = {}
      
          #store the dependencies
          for pair in task_pair_list:
              task = pair.task
              dep = pair.dependency
      
              task_deps[task].update({dep:1})
      
          #loop through list to determine order
          while(len(task_pair_list) > 0):
              delete_task = None
      
              #find a task with no dependencies
              for task in task_deps:
                  if len(task_deps[task]) == 0:
                      delete_task = task
                      print task
                      task_deps.pop(task)
                      break
      
              if delete_task == None:
                  return -1
      
              #check each task's hash of dependencies for delete_task
              for task in task_deps:
                  if delete_key in task_deps[task]:
                      del task_deps[task][delete_key]
      
          return 0
      

      如果您更新检查已完全满足的依赖项的循环以遍历整个列表并同时执行/删除不再具有任何依赖项的任务,这也应该允许您利用完成并行任务。

      【讨论】:

        猜你喜欢
        • 2018-07-07
        • 2015-02-05
        • 2012-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多