试图找出寻找下一个素数的策略:
Algo#1(平行):
private static int NextPrime(int p)
{
int nextP = 2 * p; // there is always a prime between n & 2*n !
Enumerable.Range(p + 1, p)
.AsParallel()
.ForAll(g =>
{
bool prime = true;
for (int i = 2; i <= Math.Sqrt(g); i++)
{
if (g % i == 0)
{
prime = false;
break;
}
}
if (prime)
{
if (g < nextP)
nextP = g;
return;
}
});
return nextP;
}
Algo#2(顺序):
private static int NextPrimeNonParallel(int p)
{
int nextP = 2 * p; // there is always a prime between n & 2*n !
foreach (var g in Enumerable.Range(p + 1, p))
{
bool prime = true;
for (int i = 2; i <= Math.Sqrt(g); i++)
{
if (g % i == 0)
{
prime = false;
break;
}
}
if (prime)
{
if (g < nextP)
nextP = g;
return nextP;
}
}
return 0;
}
顺序明显快于并行:)为什么?或者我的平行算法真的写得很好吗?
谢谢!
答案 0 :(得分:4)
原因是并行ForAll
在找到第一个素数时不会终止,但总是在返回之前循环整个范围,而非并行版本将返回找到它后立即获得第一个价值。
这也会使并行版本错误,因为它不会返回范围内的第一个找到的素数,但实际上 last 找到了一个,因为每次找到新的素数时它会覆盖nextP范围中。请注意,这可能不是范围中的最高素数,只是最后找到的。由于您正在运行任务,因此可能无序处理元素。
您可能想要做的是使用this version of Parallel.ForEach,每次迭代都会给您一个ParallelLoopState。如果你在该对象上调用Break()(注意:由于乱序执行而不是停止(),那么你可能会得到一个太大的值),循环将在最早的方便时中断。您还需要对nextP进行同步写入(它是所有线程之间的共享状态),以便它只保存最小值a'la;
lock(lockObject) {
if(value<nextP) nextP = value;
}
总而言之,应该允许您以更高的性能并行运行。
答案 1 :(得分:0)
@Joachim的回答是正确的。我只想补充几点:
您的素性测试算法很天真。如果你正在处理非常大的数字并且你想要一个非常快速的算法,那么有复杂但渐近更快的算法,例如参见http://en.wikipedia.org/wiki/AKS_primality_test。
如果您不想要那么多的复杂性,但想以简单的方式加速算法:
假设您要查找大于10 ^ 12的最小素数。我的建议假设你有足够的空间来存储小于10 ^ 6的所有素数。它还假设您将运行许多类似的查询,以使查找所有质数小于10 ^ 6步的值。
答案 2 :(得分:0)
并行程序慢于顺序程序的两个原因。
1:由于通信开销,即处理器或线程或同步之间的信息交换时间,以及空闲时间(处理器无法做任何有用但等待事件发生的时间),它也可以取决于每个处理器的工作分配不均等。