一尘不染

Python和C ++之间异常的速度差异

algorithm

我最近写了一个简短的算法来计算python中的快乐数字。该程序允许您选择一个上限,它将确定其下的所有快乐数字。为了进行速度比较,我决定对我知道的从python到c
++的算法进行最直接的翻译。

令人惊讶的是,c
版本的运行速度明显慢于python版本。执行时间之间的准确速度测试(用于发现前10,000个快乐数字)表明python程序平均在0.59秒内运行,而c
版本平均在8.5秒内运行。

我将这种速度差异归因于这样一个事实,即我必须为已经内置在python语言中的c
++版本的部分计算(例如,确定某个元素是否在列表/数组/向量中)编写辅助函数。 。

首先,这是造成如此荒谬的速度差异的真正原因,其次,我该如何更改c ++版本以比python版本更快地执行(我认为应该如此)。

经过速度测试的两段代码在这里:Python版本C
++版本
。谢谢您的帮助。

#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <windows.h>

using namespace std;

bool inVector(int inQuestion, vector<int> known);
int sum(vector<int> given);
int pow(int given, int power);
void calcMain(int upperBound);

int main()
{
    while(true)
    {
        int upperBound;
        cout << "Pick an upper bound: ";
        cin >> upperBound;
        long start, end;
        start = GetTickCount();
        calcMain(upperBound);
        end = GetTickCount();
        double seconds = (double)(end-start) / 1000.0;
        cout << seconds << " seconds." << endl << endl;
    }
    return 0;
}

void calcMain(int upperBound)
{
    vector<int> known;
    for(int i = 0; i <= upperBound; i++)
    {
        bool next = false;
        int current = i;
        vector<int> history;
        while(!next)
        {
            char* buffer = new char[10];
            itoa(current, buffer, 10);
            string digits = buffer;
            delete buffer;
            vector<int> squares;
            for(int j = 0; j < digits.size(); j++)
            {
                char charDigit = digits[j];
                int digit = atoi(&charDigit);
                int square = pow(digit, 2);
                squares.push_back(square);
            }
            int squaresum = sum(squares);
            current = squaresum;
            if(inVector(current, history))
            {
                next = true;
                if(current == 1)
                {
                    known.push_back(i);
                    //cout << i << "\t";
                }
            }
            history.push_back(current);
        }
    }
    //cout << "\n\n";
}

bool inVector(int inQuestion, vector<int> known)
{
    for(vector<int>::iterator it = known.begin(); it != known.end(); it++)
        if(*it == inQuestion)
            return true;
    return false;
}

int sum(vector<int> given)
{
    int sum = 0;
    for(vector<int>::iterator it = given.begin(); it != given.end(); it++)
        sum += *it;
    return sum;
}

int pow(int given, int power)
{
    int original = given;
    int current = given;
    for(int i = 0; i < power-1; i++)
        current *= original;
    return current;
}

#!/usr/bin/env python

import timeit

upperBound = 0

def calcMain():
    known = []
    for i in range(0,upperBound+1):
        next = False
        current = i
        history = []
        while not next:
            digits = str(current)
            squares = [pow(int(digit), 2) for digit in digits]
            squaresum = sum(squares)
            current = squaresum
            if current in history:
                next = True
                if current == 1:
                    known.append(i)
                    ##print i, "\t",
            history.append(current)
    ##print "\nend"

while True:    
    upperBound = input("Pick an upper bound: ")
    result = timeit.Timer(calcMain).timeit(1)
    print result, "seconds.\n"

阅读 403

收藏
2020-07-28

共1个答案

一尘不染

对于100000个元素,Python代码花费6.9秒,而C ++最初花费了37秒以上。

我对您的代码进行了一些基本的优化,并设法使C 代码比Python实现快100倍以上。现在,它可以在0.06秒内完成100000个元素。这比原始C
代码快617倍。

最重要的是在发布模式下进行所有优化。在调试模式下,这段代码实际上要慢几个数量级。

接下来,我将解释我所做的优化。

  • 将所有矢量声明移出循环;用clear()操作替换了它们,这比调用构造函数快得多。
  • 将对pow(value,2)的调用替换为一个乘法:value * value。
  • 我没有平方向量并对其求和,而仅使用整数就对值进行求和。
  • 避免了所有与整数运算相比非常慢的字符串运算。例如,可以通过重复除以10并获取结果值的模数10来计算每个数字的平方,而不是将值转换为字符串然后将每个字符转换回int。
  • 避免使用所有向量副本,首先通过将按值传递替换为按引用传递,最后避免完全消除辅助函数。
  • 消除了一些临时变量。
  • 我可能忘记了许多小细节。并行比较您的代码和我的代码,以查看我所做的工作。

通过使用预分配的数组而不是向量,可能甚至可以进一步优化代码,但这将需要更多的工作,我将其作为练习留给读者。:P

这是优化的代码:

#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <algorithm>
#include <windows.h>

using namespace std;

void calcMain(int upperBound, vector<int>& known);

int main()
{
    while(true)
    {
        vector<int> results;
        int upperBound;
        cout << "Pick an upper bound: ";
        cin >> upperBound;
        long start, end;
        start = GetTickCount();
        calcMain(upperBound, results);
        end = GetTickCount();
        for (size_t i = 0; i < results.size(); ++i) {
            cout << results[i] << ", ";
        }
        cout << endl;
        double seconds = (double)(end-start) / 1000.0;
        cout << seconds << " seconds." << endl << endl;
    }
    return 0;
}

void calcMain(int upperBound, vector<int>& known)
{
    vector<int> history;
    for(int i = 0; i <= upperBound; i++)
    {
        int current = i;
        history.clear();
        while(true)
        {
                int temp = current;
                int sum = 0;
                while (temp > 0) {
                    sum += (temp % 10) * (temp % 10);
                    temp /= 10;
                }
                current = sum;
                if(find(history.begin(), history.end(), current) != history.end())
                {
                        if(current == 1)
                        {
                                known.push_back(i);
                        }
                        break;
                }
                history.push_back(current);
        }
    }
}
2020-07-28