簡體   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