有人可以告诉我为什么这个Python Numpy代码:
import numpy as np
import time
k_max = 40000
N = 10000
data = np.zeros((2,N))
coefs = np.zeros((k_max,2),dtype=float)
t1 = time.time()
for k in xrange(1,k_max+1):
cos_k = np.cos(k*data[0,:])
sin_k = np.sin(k*data[0,:])
coefs[k-1,0] = (data[1,-1]-data[1,0]) + np.sum(data[1,:-1]*(cos_k[:-1] - cos_k[1:]))
coefs[k-1,1] = np.sum(data[1,:-1]*(sin_k[:-1] - sin_k[1:]))
t2 = time.time()
print('Time:')
print(t2-t1)
比这个C ++代码更快:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
// consts
const unsigned int k_max = 40000;
const unsigned int N = 10000;
int main()
{
time_t start, stop;
double diff;
// table with data
double data1[ N ];
double data2[ N ];
// table of results
double coefs1[ k_max ];
double coefs2[ k_max ];
// main loop
time( & start );
for( unsigned int j = 1; j<N; j++ )
{
for( unsigned int i = 0; i<k_max; i++ )
{
coefs1[ i ] += data2[ j-1 ]*(cos((i+1)*data1[ j-1 ]) - cos((i+1)*data1[ j ]));
coefs2[ i ] += data2[ j-1 ]*(sin((i+1)*data1[ j-1 ]) - sin((i+1)*data1[ j ]));
}
}
// end of main loop
time( & stop );
// speed result
diff = difftime( stop, start );
cout << "Time: " << diff << " seconds";
return 0;
}
第一个显示:&#34;时间:8秒&#34; 而第二个:&#34;时间:11秒&#34;
我知道numpy是用C语言编写的,但我仍然认为C ++示例会更快。我错过了什么吗?有没有办法改进C ++代码(或python代码)?提前感谢您的帮助!
修改 我已经按照其中一条评论中的建议更改了C ++代码(动态表到静态表)。 C ++代码现在更快,但仍然比Python版本慢得多。
EDIT2: 我已经从调试模式更改为发布模式并且增加了&#39; k&#39;从4000到40000.现在numpy稍快(8秒到11秒)。
答案 0 :(得分:9)
我发现这个问题很有意思,因为每次我遇到类似numpy速度的话题(与c / c ++相比)时,总会有答案像#34;它是一个很薄的包装器,它的核心是用c,所以它的脂肪&#34;,但这并不能解释为什么c应该比带有附加层(甚至是薄层)的c慢。
答案是:正确编译后,你的c ++代码并不比你的python代码慢。
我已经完成了一些基准测试,起初看起来numpy的速度要快得多。但是我忘了用gcc来优化编译。
我再次计算了所有内容,并将结果与纯c版代码进行了比较。我使用的是gcc版本4.9.2和python2.7.9(使用相同的gcc从源代码编译)。要编译我使用g++ -O3 main.cpp -o main
的c ++代码,要编译我使用gcc -O3 main.c -lm -o main
的c代码。在所有示例中,我用data
变量填充了一些数字(0.1,0.4),因为它改变了结果。我还更改了np.arrays以使用双精度(dtype=np.float64
),因为在c ++示例中有双精度数。我的纯c版代码(类似):
#include <math.h>
#include <stdio.h>
#include <time.h>
const int k_max = 100000;
const int N = 10000;
int main(void)
{
clock_t t_start, t_end;
double data1[N], data2[N], coefs1[k_max], coefs2[k_max], seconds;
int z;
for( z = 0; z < N; z++ )
{
data1[z] = 0.1;
data2[z] = 0.4;
}
int i, j;
t_start = clock();
for( i = 0; i < k_max; i++ )
{
for( j = 0; j < N-1; j++ )
{
coefs1[i] += data2[j] * (cos((i+1) * data1[j]) - cos((i+1) * data1[j+1]));
coefs2[i] += data2[j] * (sin((i+1) * data1[j]) - sin((i+1) * data1[j+1]));
}
}
t_end = clock();
seconds = (double)(t_end - t_start) / CLOCKS_PER_SEC;
printf("Time: %f s\n", seconds);
return coefs1[0];
}
对于以下k_max = 100000, N = 10000
结果:
Python和c ++基本上是同一时间,但请注意,有一个长度为k_max的python循环,与c / c ++相比应该慢得多。 它就是。
k_max = 1000000, N = 1000
我们有:
k_max = 1000000, N = 100
:
因此差异随着分数k_max/N
的增加而增加,但即使N
比k_max
大得多,python也不会更快,e。 G。 k_max = 100, N = 100000
:
显然,c / c ++和python之间的主要速度差异在for
循环中。但是我想找出numpy和c中数组上简单操作之间的区别。在代码中使用numpy的优点包括:1。将整个数组乘以数字,2。计算整个数组的sin / cos,3。对数组的所有元素求和,而不是分别对每个单独的项进行这些操作。所以我准备了两个脚本来比较这些操作。
Python脚本:
import numpy as np
from time import time
N = 10000
x_len = 100000
def main():
x = np.ones(x_len, dtype=np.float64) * 1.2345
start = time()
for i in xrange(N):
y1 = np.cos(x, dtype=np.float64)
end = time()
print('cos: {} s'.format(end-start))
start = time()
for i in xrange(N):
y2 = x * 7.9463
end = time()
print('multi: {} s'.format(end-start))
start = time()
for i in xrange(N):
res = np.sum(x, dtype=np.float64)
end = time()
print('sum: {} s'.format(end-start))
return y1, y2, res
if __name__ == '__main__':
main()
# results
# cos: 22.7199969292 s
# multi: 0.841291189194 s
# sum: 1.15971088409 s
C脚本:
#include <math.h>
#include <stdio.h>
#include <time.h>
const int N = 10000;
const int x_len = 100000;
int main()
{
clock_t t_start, t_end;
double x[x_len], y1[x_len], y2[x_len], res, time;
int i, j;
for( i = 0; i < x_len; i++ )
{
x[i] = 1.2345;
}
t_start = clock();
for( j = 0; j < N; j++ )
{
for( i = 0; i < x_len; i++ )
{
y1[i] = cos(x[i]);
}
}
t_end = clock();
time = (double)(t_end - t_start) / CLOCKS_PER_SEC;
printf("cos: %f s\n", time);
t_start = clock();
for( j = 0; j < N; j++ )
{
for( i = 0; i < x_len; i++ )
{
y2[i] = x[i] * 7.9463;
}
}
t_end = clock();
time = (double)(t_end - t_start) / CLOCKS_PER_SEC;
printf("multi: %f s\n", time);
t_start = clock();
for( j = 0; j < N; j++ )
{
res = 0.0;
for( i = 0; i < x_len; i++ )
{
res += x[i];
}
}
t_end = clock();
time = (double)(t_end - t_start) / CLOCKS_PER_SEC;
printf("sum: %f s\n", time);
return y1[0], y2[0], res;
}
// results
// cos: 20.910590 s
// multi: 0.633281 s
// sum: 1.153001 s
Python结果:
C结果:
正如你所看到的那样,numpy非常快,但总是比纯c慢一点。
答案 1 :(得分:3)
在我的计算机上,你的(当前)Python代码在14.82秒内运行(是的,我的计算机速度很慢)。
我将你的C ++代码重写为我认为合理的东西(基本上,我几乎忽略了你的C ++代码,只是将你的Python重写为C ++。这给了我这个:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <chrono>
#include <vector>
#include <assert.h>
const unsigned int k_max = 40000;
const unsigned int N = 10000;
template <class T>
class matrix2 {
std::vector<T> data;
size_t cols;
size_t rows;
public:
matrix2(size_t y, size_t x) : cols(x), rows(y), data(x*y) {}
T &operator()(size_t y, size_t x) {
assert(x <= cols);
assert(y <= rows);
return data[y*cols + x];
}
T operator()(size_t y, size_t x) const {
assert(x <= cols);
assert(y <= rows);
return data[y*cols + x];
}
};
int main() {
matrix2<double> data(N, 2);
matrix2<double> coeffs(k_max, 2);
using namespace std::chrono;
auto start = high_resolution_clock::now();
for (int k = 0; k < k_max; k++) {
for (int j = 0; j < N - 1; j++) {
coeffs(k, 0) += data(j, 1) * (cos((k + 1)*data(j, 0)) - cos((k + 1)*data(j+1, 0)));
coeffs(k, 1) += data(j, 1) * (sin((k + 1)*data(j, 0)) - sin((k + 1)*data(j+1, 0)));
}
}
auto end = high_resolution_clock::now();
std::cout << duration_cast<milliseconds>(end - start).count() << " ms\n";
}
这大约花了14.4秒,所以它比Python版略有改进 - 但鉴于Python对于某些C代码来说几乎是一个非常薄的包装器,只得到一点点改进就是我们应该期待的。
下一个明显的步骤是使用多个核心。要在C ++中执行此操作,我们可以添加以下行:
#pragma omp parallel for
...在外for
循环之前:
#pragma omp parallel for
for (int k = 0; k < k_max; k++) {
for (int j = 0; j < N - 1; j++) {
coeffs(k, 0) += data(j, 1) * (cos((k + 1)*data(j, 0)) - cos((k + 1)*data(j+1, 0)));
coeffs(k, 1) += data(j, 1) * (sin((k + 1)*data(j, 0)) - sin((k + 1)*data(j+1, 0)));
}
}
将-openmp
添加到编译器的命令行,这大约需要4.8秒。如果你有超过4个核心,你可能会期望比这更大的改进(相反,如果你有少于4个核心,预计会有一个较小的改进 - 但现在,超过4个更常见,更少)。
答案 2 :(得分:2)
我试图理解你的python代码并用C ++重现它。我发现你没有正确表示for循环以便正确计算 coeffs ,因此切换你的 for循环。如果是这种情况,您应该具备以下条件:
#include <iostream>
#include <cmath>
#include <time.h>
const int k_max=40000;
const int N = 10000;
double cos_k, sin_k;
int main(int argc, char const *argv[])
{
time_t start, stop;
double data[2][N];
double coefs[k_max][2];
time( & start );
for(int i=0; i<k_max; ++i)
{
for(int j=0; j<N; ++j)
{
coefs[i][0] += data[1][ j-1 ]*(cos( (i+1)*data[0][ j-1 ] ) - cos( (i+1)*data[0][ j ]) );
coefs[i][1] += data[1][ j-1 ]*(sin( (i+1)*data[0][ j-1 ] ) - sin( (i+1)*data[0][ j ]) );
}
}
// end of main loop
time( & stop );
// speed result
double diff = difftime( stop, start );
std::cout << "Time: " << diff << " seconds" << std::endl;
return 0;
}
切换for循环让我:C ++代码 3秒,用 -O3 优化,而python代码运行7.816秒