繁体   English   中英

针对Euler项目的性能差异B / n C ++和Python

[英]Performance Discrepancy B/n C++ and Python for Project Euler

我在两个相等的程序之间遇到了一个有点奇怪的性能差异,并且我不能出于任何真正的原因来推断出这种差异。

我正在解决项目Euler问题46。两种代码解决方案(一个在Python中,一个在Cpp中)都得到了正确的答案。 但是,python解决方案似乎性能更高,这与我的预期相矛盾。

不用担心实际算法是否最优-我只关心它们是两个相等的程序。 我确定有一个更优化的算法。

Python解决方案

import math
import time

UPPER_LIMIT = 1000000
HIT_COUNT = 0

def sieveOfErato(number):
    sieve = [True] * number
    for i in xrange(2, int(math.ceil(math.sqrt(number)))):
        if sieve[i]:
            for j in xrange(i**2, number, i):
                sieve[j] = False
    primes = [i for i, val in enumerate(sieve) if i > 1 and val == True]
    return set(primes)

def isSquare(number):
    ans = math.sqrt(number).is_integer()
    return ans

def isAppropriateGolbachNumber(number, possiblePrimes):
    global HIT_COUNT
    for possiblePrime in possiblePrimes:
        if possiblePrime < number:
            HIT_COUNT += 1
            difference = number - possiblePrime
            if isSquare(difference / 2):
                return True
    return False

if __name__ == '__main__':
    start = time.time()
    primes = sieveOfErato(UPPER_LIMIT)
    answer = -1
    for odd in xrange(3, UPPER_LIMIT, 2):
        if odd not in primes:
            if not isAppropriateGolbachNumber(odd, primes):
                answer = odd
                break
    print('Hit Count: {}'.format(HIT_COUNT))
    print('Loop Elapsed Time: {}'.format(time.time() - start))
    print('Answer: {}'.format(answer))

C ++解决方案

#include <iostream>
#include <unordered_set>
#include <vector>
#include <math.h>
#include <cstdio>
#include <ctime>

int UPPER_LIMIT = 1000000;

std::unordered_set<int> sieveOfErato(int number)
{
    std::unordered_set<int> primes;
    bool sieve[number+1];
    memset(sieve, true, sizeof(sieve));

    for(int i = 2; i * i <= number; i++)
    {
        if (sieve[i] == true)
        {
            for (int j = i*i; j < number; j+=i)
            {
                sieve[j] = false;
            }
        }
    }
    for(int i = 2; i < number; i++)
    {
        if (sieve[i] == true)
        {
            primes.insert(i);
        }
    }
    return primes;
}

bool isPerfectSquare(const int& number)
{
    int root(round(sqrt(number)));
    return number == root * root;
}

int hitCount = 0;

bool isAppropriateGoldbachNumber(const int& number, const std::unordered_set<int>& primes)
{
    int difference;
    for (const auto& prime : primes)
    {
        if (prime < number)
        {
            hitCount++;
            difference = (number - prime)/2;
            if (isPerfectSquare(difference))
            {
                return true;
            }
        }
    }
    return false;
}

int main(int argc, char** argv)
{
    std::clock_t start;
    double duration;
    start = std::clock();
    std::unordered_set<int> primes =  sieveOfErato(UPPER_LIMIT);

    int answer = -1;
    for(int odd = 3; odd < UPPER_LIMIT; odd+=2)
    {
        if (primes.find(odd) == primes.end())
        {
            if (!isAppropriateGoldbachNumber(odd, primes))
            {
                answer = odd;
                break;
            }
        }
    }
    duration = (std::clock() - start) / (double) CLOCKS_PER_SEC;
    std::cout << "Hit Count: " << hitCount << std::endl;
    std::cout << std::fixed << "Loop Elapsed Time: " << duration << std::endl;
    std::cout << "Answer: " << answer << std::endl;
}

我正在通过g++ -std=c++14 file.cpp编译我的cpp代码,然后仅使用./a.out执行。

在仅使用命令行中的time命令的几次测试运行中,我得到:

蟒蛇

Hit Count: 128854
Loop Elapsed Time: 0.393740177155
Answer: 5777

real    0m0.525s
user    0m0.416s
sys 0m0.049s

C ++

Hit Count: 90622
Loop Elapsed Time: 0.993970
Answer: 5777

real    0m1.027s
user    0m0.999s
sys 0m0.013s

为什么在python版本中会有更多点击,而它仍会以更快的速度返回? 我认为更多的命中,意味着更多的迭代,意味着更慢(并且在python中)。 我猜我的cpp代码中只存在性能错误,但是我还没有发现。 有任何想法吗?

我同意Kunal Puri的回答,即更好的算法和数据结构可以提高性能,但不能回答核心问题:为什么使用相同数据结构的相同算法在python中运行得更快。

归结为std::unordered_set和python的set之间的区别。 请注意,带有std::set的相同C ++代码的运行速度比python的替代方法快,并且如果启用了优化功能(使用-O2 ),则带有std::set C ++代码的运行速度将比python快10倍以上。

有几件作品表明,以及为什么std::unordered_set在性能方面被破坏了。 例如,您可以观看C ++ Now 2018:您可以比std :: unordered_map做得更好:哈希表性能的新改进 这似乎是Python并不在它的这些设计上的缺陷遭受set

使std::unordered_set如此糟糕的原因之一是,它强制执行大量直接访问元素的间接操作。 例如,在迭代过程中,迭代器指向当前存储区之前的存储区。 要考虑的另一件事是较差的缓存位置。 python的set似乎更喜欢保留元素的原始顺序,但是GCC的std::unordered_set倾向于创建随机顺序。 这是C ++和python之间的HIT_COUNT差异的原因。 一旦代码开始使用std::set则C ++和python的HIT_COUNT变为相同。 在迭代过程中保留原始顺序往往会改善新进程中节点的缓存局部性,因为它们的分配顺序与它们的分配顺序相同(新进程的两个相邻分配具有更高的机会被连续分配)内存地址)。

除了DYZ建议的编译器优化之外,我还有更多关于优化的观察。

1)使用std::vector而不是std::unordered_set

在您的代码中,您正在执行以下操作:

std::unordered_set<int> sieveOfErato(int number)
{
    std::unordered_set<int> primes;
    bool sieve[number+1];
    memset(sieve, true, sizeof(sieve));

    for(int i = 2; i * i <= number; i++)
    {
        if (sieve[i] == true)
        {
            for (int j = i*i; j < number; j+=i)
            {
                sieve[j] = false;
            }
        }
    }
    for(int i = 2; i < number; i++)
    {
        if (sieve[i] == true)
        {
            primes.insert(i);
        }
    }
    return primes;
}

我在这里看不到使用std::unordered_set任何原因。 相反,您可以这样做:

std::vector<int> sieveOfErato(int number)
{
    bool sieve[number+1];
    memset(sieve, true, sizeof(sieve));

    int numPrimes = 0;

    for(int i = 2; i * i <= number; i++)
    {
        if (sieve[i] == true)
        {
            for (int j = i*i; j < number; j+=i)
            {
                sieve[j] = false;
            }

            numPrimes++;
        }
    }

    std::vector<int> primes(numPrimes);
    int j = 0;

    for(int i = 2; i < number; i++)
    {
        if (sieve[i] == true)
        {
            primes[j++] = i;
        }
    }
    return primes;
}

find()而言,您可以这样做:

int j = 0;
for(int odd = 3; odd < UPPER_LIMIT; odd+=2)
{
    while (j < primes.size() && primes[j] < odd) {
        j++;
    }

    if (primes[j] != odd)
    {
        if (!isAppropriateGoldbachNumber(odd, primes))
        {
            answer = odd;
            break;
        }
    }
}

2)预先在std::vector预先计算理想平方 ,而不是总是调用sqrt

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM