python - 欧拉项目的性能差异 B/n C++ 和 Python
问题描述
我在两个平等的程序之间遇到了一个稍微奇怪的性能差异,我无法以任何真正的原因来推断差异。
我正在解决 Project 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;
}
我正在编译我的 cpp 代码g++ -std=c++14 file.cpp
,然后使用./a.out
.
在仅使用time
命令行中的命令进行的几次测试运行中,我得到:
Python
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
。请注意,相同的 C++ 代码std::set
运行速度比 python 的替代方案快,如果启用优化(使用-O2
),则 C++ 代码的std::set
运行速度比 python 快 10 倍以上。
有几部作品表明,以及为什么,std::unordered_set
在性能方面被破坏了。例如,您可以观看C++Now 2018: You Can Do Better than std::unordered_map: New Improvements to Hash Table Performance。似乎 python 在其set
.
造成如此糟糕的一件事std::unordered_set
是它要求大量的间接到达一个元素。例如,在迭代期间,迭代器指向当前存储桶之前的存储桶。要考虑的另一件事是较差的缓存位置。set
python 似乎更喜欢保留元素的原始顺序,但 GCC倾向于std::unordered_set
创建随机顺序。这就是HIT_COUNT
C++ 和 python 之间存在差异的原因。一旦代码开始使用,std::set
那么HIT_COUNT
对于 C++ 和 python 变得相同。在迭代期间保留原始顺序往往会提高新进程中节点的缓存局部性,因为它们以与分配相同的顺序进行迭代(并且新进程的两个相邻分配有更高的机会连续分配内存地址)。
推荐阅读
- javascript - 在 sinon 中调用另一个方法后如何测试超时的方法
- oracle - java中的oracle Tuxedo客户端
- excel - 在 Excel 中打开时消除选定范围内的双精度
- reactjs - 从 ReactJS 中的另一个文件调用函数
- xamarin - 我试图在我的异步方法中运行一个任务,但它给了我一个 ui 线程错误
- visual-studio - 我想在 Visual Studio Code 中添加一个新终端
- java - Javadoc 链接到当前方法实现,这可能吗?
- c - 循环求和字符串 C 中的数字
- html - MySql Workbench 和 Html Sublimetext 3
- php - Visual Studio Code PHP 验证错误:无法验证,因为 /usr/bin/php 不是有效的 php 可执行文件