![](/img/trans.png)
[英]C++ appears to be significantly slower than both Python Ruby for Project Euler
[英]Performance Discrepancy B/n C++ and Python for Project Euler
我在两个相等的程序之间遇到了一个有点奇怪的性能差异,并且我不能出于任何真正的原因来推断出这种差异。
我正在解决项目Euler问题46。两种代码解决方案(一个在Python中,一个在Cpp中)都得到了正确的答案。 但是,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))
#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
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.