為什麼Cython寫的擴展會比直接用C寫還快?
本來是想測試Cython比起C寫的擴展到底有多慢的。。。結果令我無法理解的是,Cython寫的竟然比直接用C還快, 代碼如下:
/* prime.c */
#include &static PyObject*
prime_is_prime(PyObject* self, PyObject* args)
{
int x = 0;
int i = 2;if (!PyArg_ParseTuple(args, "i", x)) {
return NULL;
}
if (x &<= 1) { Py_RETURN_FALSE; } while (i &< x) { if (x % i == 0){ Py_RETURN_FALSE; } i++; } Py_RETURN_TRUE; } static PyMethodDef prime_methods[] = { {"is_prime", prime_is_prime, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL}, }; PyMODINIT_FUNC initprime(void) { PyObject* module; module = Py_InitModule("prime", prime_methods); return; }
# prime.pyx
def is_prime(int x):
cdef int i = 2
if x &<= 1: return False while i &< x: if x % i == 0: return False i += 1 return True函數功能都是判斷素數,為了體現語言本身的性能差異故意用了效率很低的素數定義實現,對從1到2^17的整數調用is_prime函數,結果是(單位:秒):
我當時就凌亂了。。。是不是以後寫Python擴展都不用用C了。。。
C,Cython
1.89667,1.868518
1.918403,1.887766
1.901543,1.865995
1.894878,1.870471
1.89857,1.872739
1.921766,1.899559
1.883487,1.868034
1.898223,1.869915
1.894801,1.869459
1.886599,1.865767
1.896856,1.867167
看一下 Cython 編譯出來的代碼你會發現,它的 PyMethodDef 中 is_prime 被定義為 METH_O, 而你手寫的是 METH_VARARGS 。你手寫的版本需要額外用 PyArg_ParseTuple 來對參數進行解析,額外的時間應該是花在了這裡。
操作數字的小程序都很容易被優化的。我覺得你要做測試的時候應該選
1:C寫的版本內存用量巨大,而python的版本因為vm的關係用量更大
2:你操作的是字元串,譬如寫一個python的parser,層層遞歸
類似的句式,
「為什麼Java寫的代碼比用c寫還快?」
「為什麼c寫的代碼比用彙編寫還快?」
這並不奇怪。
推薦閱讀: