超长冗余代码是否有效?

时间:2015-03-05 20:54:50

标签: c++ performance algorithm optimization sieve-of-eratosthenes

我试图用希腊人算法的Sieve找到一些素数。我有一些效率问题。这是代码:

void check_if_prime(unsigned number)
{
    unsigned index = 0;
    while (primes[index] <= std::sqrt(number))
    {
        if (number % primes[index] == 0) return;
        ++index;
    }
    primes.push_back(number);
}

而且,因为我编写了巨大的2/3/5/7/11/13主轮,代码是5795行长。

for (unsigned i = 0; i < selection; ++i)
{
    unsigned multiple = i * 30030;
    if (i!=0) check_if_prime( multiple+1 );
    check_if_prime ( multiple+17 );
    check_if_prime ( multiple+19 );
    check_if_prime ( multiple+23 );
    // ...so on until 30029
}

优化标志:-O3,-fexpensive-optimizations,-march = pentium2

在20分钟内有2500万个素数,CPU卡在50%(不知道为什么,尝试了实时优先级,但它并没有太大变化)。输出文本文件的大小为256MB(稍后将更改为二进制文件)。

  • 编译需要很长时间!好吗?如何在不影响效率的情况下加快速度?
  • for循环开始时的if语句是否正常?如果陈述时间最长,我已经读过了。
  • 关于代码的其他任何事情,而不是算法?什么能让它更快?哪些陈述比其他陈述更快?
  • 即使是一个更大的轮子(最多510510,而不仅仅是30030,很多线路)在一天之内编译?

我想找到最多2 ^ 32的所有素数,并且很少的优化可以节省一些时间和电力。提前谢谢!

编辑:没有寻求算法,如果可以做的话,寻求代码改进!

3 个答案:

答案 0 :(得分:4)

以下是我对您的计划表现的看法:

  1. 可能您的主要问题是对std::sqrt()的调用。这是一个浮点函数,专为完全精确的结果而设计,它肯定需要相当多的周期。我敢打赌,如果你使用这张支票,你会快得多:

    while (primes[index]*primes[index] < number)
    

    这样你就可以使用整数乘法,这对于现代CPU来说是微不足道的。

  2. for()循环开头的if语句与性能无关。它没有执行足够多次。你的内部循环是check_if_prime()中的while循环。这是你需要优化的那个。

  3. 我看不出你是如何做输出的。有一些方法可以使输出严重减慢你的速度,但我不认为这是主要问题(如果它是一个问题)。

  4. 代码大小可能是个问题:您的CPU具有容量有限的指令缓存。如果您的6k行不适合第一级指令缓存,则惩罚可能会很严重。如果我是你,我会使用数据代替代码重新实现轮子,我。 E:

    unsigned const wheel[] = {1, 17, 19, 23, ...};    //add all your 6k primes here
    for (unsigned i = 0; i < selection; ++i)
    {
        unsigned multiple = i * 30030;
        for(unsigned j = 0; j < sizeof(wheel)/sizeof(*wheel); j++) {
            check_if_prime(multiple + wheel[j]);
        }
    }
    

答案 1 :(得分:0)

让它在调试器下运行,单步执行,逐个指令,并在每个点了解它在做什么,以及为什么。

这让你走在CPU的脚下,你会看到坚果程序员让你做的所有愚蠢, 你会看到你能做得更好的事情。

这是使代码尽可能快的一种方法。

程序大小本身只会影响速度,如果你的速度太快以至于缓存成为一个问题。

答案 2 :(得分:0)

这里有一些技巧可以检查数字是否为素数:

bool is_prime(unsigned int number) // negative numbers are not prime.
{
  // A data store for primes already calculated.
  static std::set<unsigned int> calculated_primes;

  // Simple checks first:
  // Primes must be >= 2.
  // Primes greater than 2 are odd.
  if (   (number < 2)
      || ((number > 2) && ((number & 1) == 0) )
  {
    return false;
  }

  // Initialize the set with a few prime numbers, if necessary.
  if (calculated_primes.empty())
  {
     static const unsigned int primes[] =
     { 2, 3, 5, 7, 13, 17, 19, 23, 29};
     static const unsigned int known_primes_quantity =
         sizeof(primes) / sizeof(primes[0]);
     calculated_primes.insert(&primes[0], &primes[known_primes_quantity]);
  }
  // Check if the number is a prime that is already calculated:
  if (calculated_primes.find(number) != calculated_primes.end())
  {
    return true;
  }

  // Find the smallest prime to the number:
  std::set<unsigned int>::iterator prime_iter =
      calculated_primes.lower_bound(number);
  // Use this value as the start for the sieve.
  unsigned int prime_candidate = *prime_iter;
  const unsigned int iteration_limit = number * number;
  while (prime_candidate < iteration_limit)
  {
     prime_candidate += 2;
     bool is_prime = true;
     for (prime_iter = calculated_primes.begin();
          prime_iter != calculated_primes.end();
          ++prime_iter)
     {
       if ((prime_candidate % (*prime_iter)) == 0)
       {
         is_prime = false;
         break;
       }
     }
     if (is_prime)
     {
       calculated_primes.insert(prime_candidate);
       if (prime_candidate == number)
       {
         return true;
       }
     }
  }
  return false;
}

注意:这是未经测试的代码,但演示了一些检查数字是否为素数的技术。