【问题标题】:Multithreading with an unpredictable tree structure [closed]具有不可预测的树结构的多线程[关闭]
【发布时间】:2020-08-22 12:31:05
【问题描述】:

用 C++ 编写的软件,只能使用标准 C++ 库。

您好,我遇到的问题如下: 我必须并行化一个软件,但多线程版本的完成时间太随机了,我的意思是 50% 的时间比单个版本快,50% 的时间慢,这是由于我认为错误的设计选择,我会喜欢你告诉我如何纠正它。

该软件基于不断增长的树结构,而不是二进制结构,每个新节点都可能是一个可能的解决方案。一旦软件找到解决方案,程序就会停止。现在的问题是,在顺序版本中,软件计算节点所遵循的路径当然总是相同的,所以它总是需要一个固定的时间来完成任务。在多线程版本中,我有一个任务池,我在其中插入节点,线程不断从任务池中获取作业并推回新节点,但计算顺序当然不是确定性的,所以有时会发生这种情况与多线程版本相比,这种工作方式会导致更多的计算,因此会导致更长的完成时间。

所以,假设有一个不断增长的树结构,给定根,然后将所有节点放入队列中,然后开始计算第一个节点,如果这是一个解决方案,则终止,否则您计算该节点并将所有结果子节点推回队列。有多种解决方案,您不知道它们在哪个节点中,因此您只需要计算它们中的每一个,直到找到解决方案。顺序版本将始终遵循相同的路径,因此在到达第一个解决方案之前总是需要计算 N 个节点,而多线程版本可能不走运,并且采用没有解决方案的不同路径,并且在更多的情况下到达第一个解决方案步骤。

您如何确保多线程版本在达到第一个解决方案之前始终执行最多 N 步?否则,如果您需要计算更多的步骤,那么拥有多个线程的优势将毫无用处。

如果需要,我会发布代码,但正如我所说,它只是一个树结构和一个队列任务池等等。

【问题讨论】:

  • 在编写 OP 正在处理的那种程序时,有很多事情可能会出错。但是,我相信有一个单一而明确的问题:“顺序版本将始终遵循相同的路径,因此在达到第一个解决方案之前总是需要计算 N 个节点,而多线程版本可能不走运,并采取不同的方式没有解决方案的路径,并且在更多步骤中达到第一个解决方案。您如何确保多线程版本在达到第一个解决方案之前始终执行最多 N 个步骤?为什么这个问题被关闭了?

标签: c++ multithreading thread-synchronization


【解决方案1】:

重新表述您的问题:我可以保证我的并行程序所花费的时间不会超过顺序计算时间吗?

简答:“这可能很困难,需要有关您的程序和问题的具体细节。”

更长的答案

我觉得你的分析不错:

所以,假设有一个不断增长的树结构,给定根,然后将所有节点放入队列中,然后开始计算第一个节点,如果这是一个解决方案,则终止,否则您计算该节点并将所有结果子节点推回队列。有多种解决方案,您不知道它们在哪个节点中,因此您只需要计算它们中的每一个,直到找到解决方案。顺序版本将始终遵循相同的路径,因此在到达第一个解决方案之前总是需要计算 N 个节点,而多线程版本可能不走运,并且采用没有解决方案的不同路径,并且在更多的情况下到达第一个解决方案步骤。

一些旁注:你用什么来实现你的队列?这个选择非常关键。如果您的多个线程无法同时从该队列中放置/删除节点,则可能存在性能瓶颈。此外,您可能不需要将探索树的每个节点都放在该队列中。如果有足够的节点让每个线程保持忙碌,你就不需要放更多。一种典型的方法是选择一个深度截止,超过该深度的树节点不会放入队列中,而是由顺序发现/生成它们的线程处理。这样的截止是否有益以及应该设置的深度取决于您的问题。

如果上述问题不是性能问题的根源,为保证您的并行版本的一个线程与您的顺序版本在同一时间找到解决方案,至少一个线程需要走相同的路径顺序线程会

如何修改您的程序以实现这一点可能会也可能不会很棘手。如果您能够根据顺序程序的遍历顺序对节点进行排序,那么您应该能够对队列中的节点进行排序,以便您的顺序程序首先探索的节点首先被多个线程占用。这将保证至少有一个线程采用您的顺序程序的路径。

在队列中给节点一个相对优先级(无论这种排序背后的含义是什么)类似于实施启发式算法。

[edit]首先,我们需要区分CPU时间已用时间。如果您有一个程序运行两个线程 1 分钟,那就是 1 分钟的经过时间和 2 分钟的 CPU 时间。在您正在解决的问题中,您正在尝试减少总“经过”时间。也不可能持续减少“CPU 时间”。这是因为所有线程完成的所有计算,直到其中一个找到解决方案是“浪费的”,因为它不参与最终结果。使用多个线程几乎总是意味着进行更多的计算(更多的 CPU 时间)。如果您的并行实现使您的运行时间显着延长,那么我怀疑您在某个地方遇到了严重的瓶颈(可能是您的共享队列?)。

其次,当我谈论路径时,我是在谈论一个线程的整个路径,包括没有导致解决方案的路径。如果你想减少这些“坏路径”,你想要的是一种启发式方法,它会让线程探索似乎更有可能首先给出解决方案的路径。如果您能够针对您的特定问题提出一个很好的启发式方法,那么它将有益于您的顺序计算和并行计算。

【讨论】:

  • “你用什么来实现你的队列?” 我使用了一个 std::list 的节点,你使用 lock_guard 得到一个节点,然后你计算节点,然后你推回列表所有生成的子节点,再次使用 lock_guard 使列表线程安全。
  • "至少一个线程需要采用与顺序线程相同的路径。"它发生了,但问题是该路径之前可能有很多错误路径,因此多线程版本可以在经过很多错误的路径,导致比顺序版本更多的计算,因此完成时间更长。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-16
相关资源
最近更新 更多