如何使用任务将此顺序递归算法转换为并行递归算法?
public static List<int> s = new List<int>(); // random integers, array size is n
public static List<int> p = new List<int>(); // random integers, array size is n
public static int n = 100;
public static int w = 10;
static void Main(string[] args)
{
G1(n, w)
}
private static int G1(int k, int r)
{
if (k == 0 || r == 0)
{
return 0;
}
if (s[k - 1] > r)
{
return G1(k - 1, r);
}
return Math.Max(G1(k - 1, r), p[k - 1] + G1(k - 1, r - s[k - 1]));
}
我有一个例子,但它太混乱了,它没有解释,也没有我的情况那么复杂。
class CustomData
{
public int TNum;
public int TResult;
}
static int F1(int n)
{
if (n > 1) return F1(n - 1) + F1(n - 2);
else return 1;
}
static int F2(int n)
{
int fibnum = 0;
if (n < 6) fibnum = F1(n);
else
{
//fibnum = F1(n - 3) + 3 * F1(n - 4) + 3 * F1(n - 5) + F1(n - 6);
int countCPU = 4;
Task[] tasks = new Task[countCPU];
for (var j = 0; j < countCPU; j++)
tasks[j] = Task.Factory.StartNew(
(Object pp) =>
{
var data = pp as CustomData; if (data == null) return;
data.TResult = F1(n - data.TNum - 3);
},
new CustomData() {TNum = j });
Task.WaitAll(tasks);
fibnum = (tasks[0].AsyncState as CustomData).TResult + 3 * (tasks[1].AsyncState as CustomData).TResult + 3 * (tasks[2].AsyncState as CustomData).TResult + (tasks[3].AsyncState as CustomData).TResult;
}
return fibnum;
}
答案 0 :(得分:1)
对于像这样的简单任务,并行化的开销相当高,所以你绝对不希望天真地并行化每次迭代。但是,样本任务相当复杂,不仅要将递归函数更改为(某种程度上)并行化的递归函数,还要找到额外的并行化选项以获得四个独立的工作者。使用过时的多线程结构也使问题变得更加复杂。考虑一个更简单的例子:
static int F2(int n)
{
if (n <= 1) return 1;
var a = Task.Run(() => F1(n - 1));
var b = Task.Run(() => F1(n - 2));
return a.Result + b.Result;
}
我们将原始工作量(相当简单)分成两个分支。由于两个分支的工作负载大致相似,因此我们可以有效地使用两个线程。请注意,这是非常愚蠢 - 你计算两次相同的东西,并使用两个线程来完成单个线程可以做的工作量(即使没有更深入地了解递归函数,如斐波纳契序列)只是通过缓存给定n
的结果。
但是我会假设练习的目的是展示如何将任务并行化,无论如何实现这些任务的并行化(即你期望)是愚蠢和天真)。我们是如何从双线程版本到四线程版本的?跳过第一次迭代,然后立即开始第二次迭代。这为您提供了四个分支,而不是原始分支。
假设您有n > 6
(在示例中,F1
被用作特殊情况)。 F1的第一次迭代大致为:
return F1(n - 1) + F1(n - 2);
但我们希望将其与第二次迭代合并,以允许四向并行化。这就像用F1(n)
代替F1(n - 1) + F1(n - 2)
:
return F1((n - 1) - 1) + F1((n - 1) - 2) + F1((n - 2) - 1) + F1((n - 2) - 2);
可以简化为
return F1(n - 2) + F1(n - 3) + F1(n - 3) + F1(n - 4);
进一步
return F1(n - 2) + 2 * F1(n - 3) + F1(n - 4);
糟糕!我们失去了一个分支机构。所以我们实际上需要另一个替换:
return
F1((n - 2) - 1) + F1((n - 2) - 2)
+ 2 * (F1((n - 3) - 1) + F1((n - 3) - 2))
+ F1((n - 4) - 1) + F1((n - 4) - 2);
哪个适用于......
return
F1(n - 3) + F1(n - 4)
+ 2 * F1(n - 4) + 2 * F1(n - 5)
+ F1(n - 5) + F1(n - 6);
最终让我们进入了四个分支:
return
F1(n - 3) + 3 * F1(n - 4) + 3 * F1(n - 5) + F1(n - 6);
每个分支都可以天真地并行运行,并且可以很好地利用所有四个线程。
您现在可以将相同的推理应用于G1
并获得该特定递归函数的四向并行化。
答案 1 :(得分:0)
@Luaan这是我的解决方案,使用两个线程。我不知道它是否正常,但是顺序和并行算法的结果是匹配的,但是时间的减少极少,可能需要使用更多的线程?
private static int G2(int k, int r)
{
if (k == 0 || r == 0) return 0;
if (s[k - 1] > r) // this part gives the wrong results :(
{
Task<int> task1 = Task.Run(() => G1(k - 2, r));
Task<int> task2 = Task.Run(() => G1(k - 3, r));
return task1.Result + task2.Result;
}
Task<int> max1 = Task.Run(() => G1(k - 1, r));
Task<int> max2 = Task.Run(() => p[k - 1] + G1(k - 1, r - s[k - 1]));
return Math.Max(max1.Result, max2.Result);
}
如果(s [k - 1]&gt; r)块给出错误的结果:(