在将其转换为C ++之前,我一直在使用Python做一些快速原型设计,发现在某些情况下Python代码的运行速度明显快于C ++代码!

考虑用Python和C ++编写的这个简单循环:


python:

import numpy as np
import datetime

N = 16777216
f_s = 8000.0
t_s = 1/f_s

y = np.empty(N)

start = datetime.datetime.now()

for n in range(0,N):
    y[n] = np.sin(2*np.pi*1000*n*t_s) + 0.5*np.sin(2*np.pi*2000*n*t_s + 3*np.pi/4)

stop = datetime.datetime.now()
duration = stop - start

print("duration ", duration.microseconds, " microseconds")

输出:

duration 842000 microseconds


C ++:

#include <chrono>
#include <cmath>
#include <iostream>
#include <vector>

int main() {
    int N = 16777216;
    int f_s = 8000;
    double t_s = 1.0 / f_s;

    std::vector<double> x(N);

    auto start = std::chrono::high_resolution_clock::now();

    for (int n = 0; n < N; ++n)
    {
            x[n] = std::sin(2 * M_PI * 1000 * n * t_s) + 0.5 * std::sin(2 * M_PI * 2000 * n * t_s + 3 * M_PI / 4);
    }

    auto stop = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);

    std::cout << "duration " << duration.count() << " microseconds." << std::endl;
}

输出:

duration 1993000 microseconds.


Python代码确实需要花费大量时间来启动,而C ++代码会立即运行。 (也许Python解释器在启动时做了一些需要很长时间的优化?)但即使抛开这个事实,一旦Python代码运行,循环本身在Python中运行得更快。

这让我感到非常惊讶,但我似乎无法弄清楚Python如何快速运行这个循环。我甚至尝试在不同的优化级别编译C ++代码,看看编译器是否只是在优化方面做得不好。上面的C ++示例是使用g++ -O3编译的,以改进优化。当它没有削减它时,我甚至尝试了g++ -Ofast,它将运行时间提高到1205000微秒,但STILL显着慢于Python环路!

我试过谷歌搜索但我找不到任何真正的解释......怎么会发生这种情况?如何从C ++循环中获得更好的性能?如果不是更快,我希望我能像Python循环一样快地到达LEAST。


我正在运行Python 3.7.2

使用g++.exe (MinGW.org GCC-6.3.0-1) 6.3.0-O3编译器开关编译C ++示例以改进优化。

我还尝试在linux环境中使用g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36)编译C ++代码,结果类似。

分析解答

您没有正确使用timedeltamicroseconds组件。它只给出时间测量的sub-second微秒部分,而不是总经过的微秒。此代码突出显示您的错误

from datetime import datetime, timedelta

start = datetime(2019, 1, 1, 12, 0, 0, 0 )
end   = datetime(2019, 1, 1, 12, 0, 1, microsecond=500000 )
diff = end - start

print('Total duration=',diff)
print('Total seconds=', diff.total_seconds())
print('microseconds=', diff.microseconds)
# output
Total duration= 0:00:01.500000
Total seconds= 1.5
microseconds= 500000

使用total_seconds并乘以1e6,或者只需更改测试即可在几秒钟内报告结果。

注意C ++应该很容易赢得这个。您的循环具有固定数量的迭代,这些迭代在编译时是已知的,并且所有计算都不相互依赖。一个好的优化C ++编译器应该对该循环进行chunkify或部分展开,并使用SSE指令并行执行小型计算。