问题描述
用C ++编写的软件只能使用标准C ++库。
嗨,我面临的问题如下: 我必须并行化软件,但是多线程版本的完成时间太随机了,我的意思是50%的时间比单个版本的速度快,而50%的时间慢,这是由于我认为设计选择错误,我会希望您向我展示如何纠正它。
该软件基于Tree结构,而不是二进制结构,并且不断增长,每个新节点都可能是一种解决方案。一旦软件找到解决方案,程序就会停止。现在的问题是,在顺序版本中,软件用来计算节点的路径当然总是相同的,因此完成任务始终需要固定的时间。在多线程版本中,我有一个任务池,我在其中插入了节点,线程不断从任务池中获取作业并推回新节点,但是计算顺序当然不是确定性的,因此有时会发生这种情况这种工作方式会导致多线程版本进行更多的计算,从而导致更长的完成时间。
因此,假设有一个不断增长的Tree结构,给出了根,然后将所有节点放入队列中,然后开始计算第一个节点,如果这是一种解决方案,则终止,否则请对该节点进行计算机处理,将所有结果子节点推回队列。解决方案有多种,您不知道它们在哪个节点上,因此您只需要计算每个解决方案,直到找到解决方案。顺序版本将始终遵循相同的路径,因此在到达第一个解决方案之前始终需要计算N个节点,而多线程版本可能很不幸,并且会采用没有解决方案的差异路径,而在更多情况下会到达第一个解决方案步骤。
如何才能确保多线程版本在达到第一个解决方案之前始终执行最多N个步骤?否则,如果您需要计算更多的步骤,那么拥有多个线程的优势将毫无用处。
如果需要,我将发布代码,但正如我所说的那样,它只是一个树结构和一个队列任务池等。
解决方法
重新表达您的问题:我能保证我的并行程序花费的时间不超过顺序计算时间吗?
简短答案:“这可能很困难,并且需要有关您的程序和问题的详细信息。”
更长的答案:
我认为您的分析相当出色:
因此,假设有一个不断增长的Tree结构,给出了根,然后将所有节点放入队列中,然后开始计算第一个节点,如果这是一种解决方案,则终止,否则请对该节点进行计算机处理,将所有结果子节点推回队列。解决方案有多种,您不知道它们在哪个节点上,因此您只需要计算每个解决方案,直到找到解决方案。顺序版本将始终遵循相同的路径,因此在到达第一个解决方案之前始终需要计算N个节点,而多线程版本可能很不幸,并且会采用没有解决方案的差异路径,而在更多情况下会到达第一个解决方案步骤。
一些注意事项:您用于实现队列的内容是什么?这个选择非常关键。如果您的多个线程不能同时从该队列中放入/删除节点,则可能存在性能瓶颈。另外,您可能不需要将探索树的每个节点都放入该队列中。如果有足够的节点来保持每个线程繁忙,则无需放置更多线程。一种典型的方法是选择一个深度截止点,超过该深度截止点,树节点将不会放置在队列中,而是由顺序发现/生成它们的线程进行处理。这样的分界是否有益,应该设定的深度取决于您的问题。
提供上述问题并非性能问题的根源,为保证并行版本的一个线程与顺序版本同时找到解决方案,至少一个线程需要采用与以下版本相同的路径顺序线程会。
如何修改程序以实现此目的可能并不容易。如果您能够根据顺序程序的遍历顺序对节点进行排序,则应该能够对队列中的节点进行排序,以便顺序程序本应首先探索的那些节点被多个线程优先处理。这样可以确保至少有一个线程采用了顺序程序所具有的路径。
为节点赋予队列中的相对优先级(无论此顺序的含义是什么)都类似于实现启发式。
[edit] 首先,我们需要区分 CPU时间和已用时间。如果您有一个程序在两个线程上运行1分钟,则这是1分钟的经过时间和2分钟的CPU时间。在您要解决的问题中,您试图减少总的“经过”时间。同样不可能持续减少“ CPU时间”。那是因为在所有线程中找到一个解决方案之前,所有线程完成的所有计算都被“浪费”了,因为它不参与最终结果。使用多个线程几乎总是意味着需要进行更多的计算(更多的CPU时间)。如果您的并行实现使您的运行时间大大延长,那么我怀疑您的某个地方存在严重的瓶颈(也许是您的共享队列?)。
第二,当我谈论路径时,我在谈论线程的整个路径,包括没有导致解决方案的路径。如果要减少这些“不良路径”,您需要的是一种启发式方法,它将使线程探索似乎更可能首先提供解决方案的路径。如果您能够针对特定问题提出很好的启发式方法,那么对顺序计算和并行计算都将有利。