繁体   English   中英

为什么我的python / numpy示例比纯C实现更快?

[英]Why is my python/numpy example faster than pure C implementation?

我在python和C中有几乎相同的代码。Python示例:

import numpy
nbr_values = 8192
n_iter = 100000

a = numpy.ones(nbr_values).astype(numpy.float32)
for i in range(n_iter):
    a = numpy.sin(a)

C示例:

#include <stdio.h>
#include <math.h>
int main(void)
{
  int i, j;
  int nbr_values = 8192;
  int n_iter = 100000;
  double x;  
  for (j = 0; j < nbr_values; j++){
    x = 1;
    for (i=0; i<n_iter; i++)
    x = sin(x);
  }
  return 0;
}

当我运行两个示例时,会发生一些奇怪的事情:

$ time python numpy_test.py 
real    0m5.967s
user    0m5.932s
sys     0m0.012s

$ g++ sin.c
$ time ./a.out 
real    0m13.371s
user    0m13.301s
sys     0m0.008s

看起来python / numpy比C快两倍。上面的实验中是否有任何错误? 你怎么解释呢?

PS我有Ubuntu 12.04,8G ram,核心i5 btw

首先,打开优化。 其次,微妙很重要。 您的C代码绝对不是“基本相同”。

这是等效的C代码:

sinary2.c:

#include <math.h>
#include <stdlib.h>

float *sin_array(const float *input, size_t elements)
{
    int i = 0;
    float *output = malloc(sizeof(float) * elements);
    for (i = 0; i < elements; ++i) {
        output[i] = sin(input[i]);
    }
    return output;
}

sinary.c:

#include <math.h>
#include <stdlib.h>

extern float *sin_array(const float *input, size_t elements)

int main(void)
{
    int i;
    int nbr_values = 8192;
    int n_iter = 100000;
    float *x = malloc(sizeof(float) * nbr_values);  
    for (i = 0; i < nbr_values; ++i) {
        x[i] = 1;
    }
    for (i=0; i<n_iter; i++) {
        float *newary = sin_array(x, nbr_values);
        free(x);
        x = newary;
    }
    return 0;
}

结果:

$ time python foo.py 

real    0m5.986s
user    0m5.783s
sys 0m0.050s
$ gcc -O3 -ffast-math sinary.c sinary2.c -lm
$ time ./a.out 

real    0m5.204s
user    0m4.995s
sys 0m0.208s

程序必须分成两部分的原因是愚弄了优化器。 否则,它将意识到整个循环根本没有效果,并对其进行优化。 将内容放入两个文件并不能使编译器在编译main时可以sin_array可能产生的副作用,因此必须假定它实际上有一些并重复调用它。

您的原始程序根本不等效,原因有几个。 一种是在C版本中有嵌套循环,而在Python中则没有。 另一个是您正在使用Python版本而不是C版本的值数组。 另一个是要在Python版本而不是C版本中创建和丢弃数组。 最后,您在Python版本中使用float ,在C版本中使用double

简单地调用sin函数适当的次数并不能进行等效的测试。

此外,优化器对于C来说确实意义非凡。当您想知道速度比较是错误的事情时,将未使用优化器的C代码进行比较。 当然,您还需要注意。 C优化器非常复杂,如果您要测试的东西实际上什么也没做,那么C优化器可能会注意到这一事实,根本不做任何事情,从而导致程序运行速度异常高。

因为“ numpy”是专用于速度的数学库。 C具有sin / cos的标准功能,通常出于准确性而派生。

您也没有将苹果与苹果进行比较,因为您在C语言中使用double,在python中使用float32(float)。 如果我们改为更改python代码以计算float64,则我的计算机上的时间将增加约2.5秒,从而使其与正确优化的C版本大致匹配。

如果整个测试是为了做更复杂的事情而需要更多的控制结构(if / else,do / while等),那么您可能会发现C和Python之间的区别甚至更少-因为C编译器无法真正做到更快地“罪过”-除非实现更好的“罪过”功能。

较新的想法是,您的代码在两侧都不完全相同...;)

您似乎在C 8192 x 10000次中执行了相同的操作,但是在python中只有10000次(我之前没有使用过numpy,所以我可能会误解代码)。 为什么在python情况下使用数组(再次,我不习惯numpy,所以解引用是隐式的)。 如果要使用数组,请注意双精度在缓存和优化矢量化方面会降低性能-您在两种实现之间使用了不同的类型(浮点数与双精度),但是考虑到算法,我认为这并不重要。

围绕C与Pythis,Pythat出现许多异常性能基准测试问题的主要原因是,仅仅C的实现常常很差。

https://www.ibm.com/developerworks/community/blogs/jfp/entry/A_Comparison_Of_C_Julia_Python_Numba_Cython_Scipy_and_BLAS_on_LU_Factorization?lang=en

如果您注意到该家伙写C来处理一个双精度数组(不使用他可能会使用的strict或const关键字),那么他会进行优化构建,然后强制编译器使用SIMD而不是AVE。 简而言之,如果编译器想要提高性能,它也会使用低效的指令集来执行双精度运算和错误的寄存器类型-您可以确定numba和numpy将使用尽可能多的钟声和哨声,并且将附带非常高效的C语言和C ++库开始。 简而言之,如果您想提高C的速度,则必须考虑它,您甚至可能不得不反汇编代码,并可能禁用优化功能,而改用编译器内部指令。 它为您提供了执行此操作的工具,因此不要指望编译器为您完成所有工作。 如果您想要那种自由度,请使用Cython,Numba,Numpy,Scipy等。它们的速度非常快,但是您将无法从机器中获得所有性能-使用C,C ++或新版本版本的FORTRAN。

这是一篇关于这些要点的很好的文章(我将使用SciPy):

https://www.scipy.org/scipylib/faq.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM