我目前正在开发一个open-std proposal来为我正在处理的项目带来并行功能,但我遇到了find_end
的路障。
现在find_end
可以描述为:
一种算法,用于搜索元素中[s_first,s_last]元素的最后一个子序列 范围[第一,最后)。第一个版本使用operator ==来比较 元素,第二个版本使用给定的二元谓词p。
它的要求由cppreference列出。现在我没有问题并行find
/ findif
/ findifnot
等等。这些可以很容易地分成异步执行的单独分区,我没有遇到任何麻烦。 find_end
的问题是将算法拆分成块不是解决方案,因为如果我们说一个向量:
1 2 3 4 5 1 2 3 8
我们要搜索1 2
。
好的,首先我将矢量异步分隔成块,然后只搜索每个块中的范围吧?看起来很容易,但是如果由于某种原因只有3个可用核心会发生什么,所以向量被分成3个块:
1 2 3
| 4 5 1
| 2 3 8
现在我遇到了问题,第二个1 2
范围被拆分为不同的分区。对于有x
个核心最终将搜索结果拆分为y
个不同分区的人来说,这会导致很多无效结果。我想我会在递归样式搜索中做某种search chunks -> merge y chunks into y/2 chunks -> search ->
,但这看起来效率很低,这个算法的重点是提高效率。我也许会过度思考这种考验
tl; dr ,有没有办法以我不想的方式并行化find_end
?
答案 0 :(得分:6)
是的,有办法。
让N
为您要查找的范围的大小。
一旦你将你的矢量分成3个块(3个独立的工作线程):
1 2 3|4 5 1|2 3 8
你可以允许每个线程在其右边相邻的块(如果有的话)上运行N-1个元素(因为序列只涉及读取操作,这非常好并且是线程安全的。)
在这种情况下:(N = 2)
核心1在1 2 3 4
核心2在4 5 1 2
Core 3在2 3 8
答案 1 :(得分:2)
由于find_end
的目的是在大海捞针中找到针头的 last ,因此通过将草垛分割成连续的段来进行并行化通常不会产生任何好处,因为如果针实际上在最后一段中,除了分配给最后一段之外的所有处理器完成的工作都被浪费了,时间与单个处理器的时间完全相同。从理论上讲,并行评估可以限制最大搜索时间,如果(1)处理器不竞争其他任务,并且(2)大海捞针的实例相对较少,这将是有益的。
此外,您需要能够协调流程终止;每个进程在找到匹配时或者当其年幼的兄弟找到匹配或放弃搜索时都可以放弃搜索。一旦进程0找到匹配或用尽地点查找它们,匹配的最低索引进程就会获胜。
另一种方法是交错搜索。如果您有k
个处理器,那么处理器0将被赋予以last-0
,last-k
,last-2k
...结尾的序列,处理器1被赋予结束的序列last-1
,last-k-1
,last-2k-1
...通常处理器i
(0 ≤ i < k
)适用于last-i
,last-k-i
, last-2k-i
...
流程协调与第一种备选方案略有不同。同样,每个单独的过程一旦找到匹配就可以停止。此外,任何进程可以在其当前目标小于另一个进程找到的最新匹配时立即停止。
虽然这应该导致搜索的合理并行化,但我不清楚它会比非并行线性时间算法(如Knuth-Morris-Pratt或Boyer-Moore)做得更好,其中任何一个都可以通过简单的修改来从右向左搜索。这些算法在针是编译时常量的非常罕见的情况下特别有用,允许预先计算必要的移位表。非交错并行化可以从KMP或BM中获益,但同样需要注意:很可能大多数参与过程都证明没有用处。