对 Q2 使用多线程的一种方法是分两次执行(每次在内部使用 T 个线程,其中 T 可以自由选择):
以通常的多线程方式计算c[i] = a[i] + b[i] + c[i-1]所有单元格,即将输入划分为范围 [0,r1), [r1,r2), ... [rk,n) 并将一个线程应用于每个范围。是的,这对于除第一个范围之外的所有范围都是不正确的,步骤 2 将更正它。
再次以多线程方式计算校正。为此,我们查找每个范围的最右边的值,即corr1:=c[r1-1],corr2:=corr1+c[r2-1]等corr3:=corr2+c[r3-1],这为我们提供了每个线程的校正值,然后再次使用与以前相同范围的多线程计算,c[i] += corrk哪里corrk是第 k 个线程的线程特定校正值。(对于第零个线程,我们可以使用corr0:=0,因此该线程不需要做任何事情。)
这将理论运行时间提高了一个因子 T,其中 T 是线程数(可以自由选择),因此就多线程而言,这是一个最佳解决方案。
为了说明这是如何工作的,这里是一个我们假设数组长度的例子n==30。我们进一步假设我们使用 3 个线程:一个用于计算范围c[0..9],一个用于计算,一个c[10..19]用于计算c[20..29]。
显然,目标是在 cell 中c[i],对于 any 0<i<n,我们得到
c[i] == a[0]+...+a[i]+b[0]+...+b[i]
(即 alla[0..i]和 all的总和b[0..i])在算法完成后。让我们看看算法是如何到达那里的,例如 cell i==23。此单元格由第三个线程处理,即负责范围的线程c[20..29]。
第 1 步:线程集
c[20] = a[20]+b[20]
c[21] = c[20]+a[21]+b[21] == a[20]+a[21]+b[20]+b[21]
c[22] = c[21]+a[22]+b[22] == a[20]+a[21]+a[22]+b[20]+b[21]+b[22]
c[23] = c[22]+a[23]+b[23] == a[20]+a[21]+a[22]+a[23]+b[20]+b[21]+b[22]+b[23]
...
因此,在第 1 步完成后,我们有了一些 ofa[20..23]和b[20..23]in cell c[23]。缺少的是 和 的a[0..19]总和b[0..19]。
类似地,第一个和第二个线程已经将值设置为c[0..9]andc[10..19]使得
c[0] = a[0]+b[0]
c[1] = c[0]+a[1]+b[1] == a[0]+a[1]+b[0]+b[1]
...
c[9] = a[0]+...+a[9]+b[0]+...+b[9]
和
c[10] = a[10]+b[10]
...
c[19] = a[10]+...+a[19]+b[10]+...+b[19]
步骤2:第三个线程的校正值,corr2是第二个线程计算的最右边值的总和corr1,而corr1第一个线程计算的最右边的值。因此
corr2 == c[9]+c[19] == (a[0]+...+a[9]+b[0]+...+b[9]) + (a[10]+...+a[19]+b[10]+...+b[19])
这确实是c[23]第 1 步之后的值缺失的总和。在第 2 步中,我们将此值添加到所有元素c[20..29],因此,在第 2 步完成后,c[23]它是正确的(所有其他单元格也是如此)。
这样做的原因是单元格值的计算是从左到右的严格增量操作,并且计算单个单元格的操作顺序无关紧要(因为+是关联的和可交换的)。因此,步骤 1 之后任何给定线程的最终结果(“最右边的值”)可用于更正步骤 2 中负责其右侧范围的线程的结果。