并行不比顺序快?

时间:2012-03-11 12:21:11

标签: c#-4.0 primes

试图找出寻找下一个素数的策略:

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;
    }

顺序明显快于并行:)为什么?或者我的平行算法真的写得很好吗?

谢谢!

3 个答案:

答案 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

  • 如果您不想要那么多的复杂性,但想以简单的方式加速算法:

    1. 检查M的素数时,只将它除以2..sqrt(M)中的素数
    2. 首先找到2..sqrt(M)中的所有素数。
    3. 在第二步中记住列表以加快将来的查询。
    4. 假设您要查找大于10 ^ 12的最小素数。我的建议假设你有足够的空间来存储小于10 ^ 6的所有素数。它还假设您将运行许多类似的查询,以使查找所有质数小于10 ^ 6步的值。

答案 2 :(得分:0)

并行程序慢于顺序程序的两个原因。

1:由于通信开销,即处理器或线程或同步之间的信息交换时间,以及空闲时间(处理器无法做任何有用但等待事件发生的时间),它也可以取决于每个处理器的工作分配不均等。

  1. 根据Amdahls的说法,如果程序的一部分不能并行化,那么无论处理器数量增加,因为最小执行时间不能小于执行的连续分数时间。