為什麼Python比C++慢很多?

如題

題主是演算法競賽選手

用Python與C++寫了一模一樣的Dfs解決n皇后(位運算)

而 n = 15時,Python跑了兩分鐘,C++只用3.3秒出結果。

為什麼呢?

Dfs函數內部邏輯都一樣,為什麼會速度不同?

Python慢在何處?

題主這裡

附上代碼 (話說很多答案的代碼寫得不優啊)

C++ :

# include &

int n = 15 ;

int cnt ;

void Dfs ( int row, int shu, int pie, int na ) {

int ave = ((1 &<&< n) - 1) ~(shu | pie | na) ;

while ( ave ) {

int p = ave -ave ;

ave ^= p ;

if ( row == n )

++ cnt ;

else

Dfs ( row + 1, shu | p, (pie | p) &>&> 1, (na | p) &<&< 1 ) ;

}

}

int main ( ) {

Dfs ( 1, 0, 0, 0 ) ;

printf ( "%d
", cnt ) ;

}

Python :

n = 15

cnt = 0

def Dfs ( row, shu, pie, na ) :

global cnt

ave = ((1 &<&< n) - 1) ~(shu | pie | na)

while ave :

p = ave -ave

ave ^= p

if row == n :

cnt += 1

else :

Dfs ( row + 1, shu | p, (pie | p) &>&> 1, (na | p) &<&< 1 )

Dfs ( 1, 0, 0, 0 )

print ( cnt )


話說為什麼大家會集中討論GIL?在這裡題主的標準線是一個按bit處理的單線程DFS啊……幾乎沒有GIL發揮的餘地好么……

這個八皇后的DFS,我的C++代碼在不加某些評估性剪枝的情況下對15需要算18s左右(開O2大約8.6秒,與題主描述基本一致),但是可以確定的是你的解決方案里用了循環與遞歸。接下來需要分析的無非是Python慢在哪個細節,以及能否改進的問題。

下面是兩段用來測試的代碼,首先是Python的:

#!/usr/bin/env python3

import time

def calc(n, i=0, cols=0, diags=0, trans=0):
if i == n:
return 1
else:
rt = 0
for j in range(n):
col = 1 &<&< j diag = 1 &<&< (i - j + n - 1) tran = 1 &<&< (i + j) if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: rt += calc(n, i+1, cols | col, diags | diag, trans | tran) return rt if __name__ == "__main__": t = time.time() print(calc(13)) print(time.time() - t)

以及C++代碼:

#include &
#include &

using namespace std;

long calc(int n, int i = 0, long cols = 0, long diags = 0, long trans = 0) {
if (i == n) {
return 1;
} else {
long rt = 0;
for (int j = 0; j &< n; j++) { long col = (1 &<&< j); long diag = (1 &<&< (i - j + n - 1)); long tran = (1 &<&< (i + j)); if (!(col cols) !(diag diags) !(tran trans)) { rt += calc(n, i + 1, col | cols, diag | diags, tran | trans); } } return rt; } } int main() { auto t = chrono::system_clock::now(); cout &<&< calc(13) &<&< endl; cout &<&< (chrono::system_clock::now() - t).count() * 1e-6 &<&< endl; return 0; }

這裡的C++代碼沒按照OOP去寫,怎麼簡單怎麼來吧……

測試機器配置是Core i7 4870HQ,編譯器用的Clang++ 8.1.0,Python解釋器則是CPython 3.6.0。沒測試15的數據量只測試一下13,因為15太費時間了……

由於這裡壓根不涉及多線程問題,那基本上就跟GIL沒有半毛錢關係了。

對於n=13,C++代碼跑了0.48秒。為了確保不是編譯器悄悄幹了活,我特地打成了-O0(實際上開O2能到0.2秒左右)。Python跑了24秒。

對於這個例子,最直接的影響其實在於:Python是逐句解釋執行的,C++是先編譯成本地代碼,期間還有編譯期的類型檢查,不存在動態類型、動態檢查,並且可以進行編譯器優化。

之後應該考慮一下能不能提高一點點效率呢?

然後根據一般規律,Python的循環很慢,我們可以考慮改成列表展開:

def calc(n, i=0, cols=0, diags=0, trans=0):
if i == n:
return 1
else:
return sum(
[
calc(n, i + 1, cols | (1 &<&< j), diags | (1 &<&< (i - j + n - 1)), trans | (1 &<&< (i + j))) for j in range(n) if (cols (1 &<&< j)) == 0 and (diags (1 &<&< (i - j + n - 1))) == 0 and (trans (1 &<&< (i + j))) == 0 ] )

理應速度更快,實時也驗證了:這樣的Python代碼需要跑18秒左右。仍然存在數量級的差異,並沒有解決根本問題,但是說明了一點,CPython中for loop的實現其實一點都不快。

而後考慮一下,如果我們使用其它解釋器,特別是包含JIT的解釋器,它將在執行過程中嘗試將代碼編譯成本地二進位編碼並執行,同時還能賦予一些額外優化,會不會好很多?

那麼單純地嘗試一下PyPy3(5.8.0-beta, Python 3.5.3),代碼能有多快?

實際上,單純的只是替換一下解釋器,換成PyPy來做的話,原本這個24s的Python源碼就只需要1s左右了。單單一個JIT可以使得性能提升一個數量級,充分說明官方的CPython解釋器的性能真心很爛……

PyPy的JIT比較簡單純粹,並不是很激進,但是同樣的代碼如果能藉助更好的JIT,以及更高性能的庫,則可以體現出完全不同的性能差。例如,如果使用llvm做JIT,同時加上能使用一些成熟的數學庫做優化。我們知道NumPy這樣的C擴展能夠很大程度提高Python做數值計算的性能,同樣的我們也可以用Cython或者直接用C寫Python擴展來強化計算能力。但是人都是懶的,重新寫代碼實在是有些麻煩。對於Python這種生態強大的玩意來說,如果你的計算代碼中只是單純的使用了numpy的簡單結構以及Python自身的標準結構,使用numba可能是最簡單快速的辦法。

#!/usr/bin/env python3

import time

from numba import jit

@jit
def calc(n, i=0, cols=0, diags=0, trans=0):
if i == n:
return 1
else:
rt = 0
for j in range(n):
col = 1 &<&< j diag = 1 &<&< (i - j + n - 1) tran = 1 &<&< (i + j) if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: rt += calc(n, i+1, cols | col, diags | diag, trans | tran) return rt if __name__ == "__main__": t = time.time() print(calc(13)) print(time.time() - t)

這裡只是很簡單地加入了兩行代碼:從numba導入jit,用jit裝飾我們的計算函數。這段代碼的運行時間直接就縮短到了0.4s,和C++版本的O0編譯後的程序速度幾乎一樣。這還是考慮到JIT需要預熱的情況在內。這段代碼,若是計算15的規模,只需要6.5s左右,甚至優於開O2的C++版本。

究其原因,JIT不僅僅在運行過程中將代碼轉為本地機器碼,同時還會嘗試進行優化。如果用cProfile之類的玩意分析一下運行過程,可以清楚看到這個優化過程。


相似的問題真的已經很多了吧,為什麼要一遍一遍的問……

Lua 的速度為什麼比 Python 快?

  1. 編譯語言和解釋語言,從本質上來說就是完全不同的:編譯語言能最終直接對應到機器碼。C/C++是典型的編譯語言。
  2. 有JIT的解釋語言和真·解釋語言,從實現上來說完全不同:JIT可以通過技術手段將解釋語言進行重新整理,變成適合編譯的結構,然後再轉換成機器碼,得到接近於編譯語言的性能。典型的JIT語言是Java、Javascript
  3. 同樣是解釋語言,抽象層次低的語言會比抽象層次高的語言實現起來更容易,運行也更快,典型的如Lua

而CPython不巧是那個解釋的、沒有JIT的、而且抽象層次很高的語言。

所以首先CPython就不該跟C++比,最應該比的是Javascript,它跟Javascript比起來最大的負擔在於與CPython兼容,比如說支持C API——CPython有一組規定好的數據結構和介面用來讓Python和C進行互操作,這個介面保證了Python可以調用C,C也能調用Python,但是這嚴重妨礙了JIT引擎的設計;再比如CPython的引用計數與標記-掃描混用的GC演算法。


Python比 C++執行效率低是多種原因交織在一起的原因。

我覺得關鍵問題是 動態類型、解釋執行、虛擬機、GIL這四個方面的問題:

1、為了支持動態類型,Python對象加入了很多抽象,執行的時候要不斷的判斷數據類型,帶來很大的開銷

2、python代碼由解釋器逐條解釋執行(interactive model)或每次執行都要先翻譯再運行,運行效率大大降低。

3、虛擬機帶來間接開銷,而且Python的虛擬機cpython性也不如jvm。

4、GIL帶來的偽多線程問題,所以python大力發展協程和非同步語法

C++是直接編譯成可執行二進位代碼,有系統原生的進程、線程加持,沒有動態類型的開銷,沒有虛擬機的開銷,如果沒有使用虛函數、虛繼承等特性,C++代碼能獲得C語言一樣的效率。

同樣運行在虛擬機上的java,編譯成位元組碼再執行,效率就高多了,重IO的場景與C++相比也不遑多讓。

Python的另一個解釋器PyPy引入了JIT、多線程等,執行效率蹭蹭的就上去了。


3秒對兩分鐘這個比例,其實對py還算有點優勢的,因為實際中的很多場景py會慢更多倍數。。。

實現方面的原因就先不提了,比如py也有能編譯為二進位的(比如Cython的模式),而C++也有解釋執行的實現,但可以確定的是,就算用同樣的實現方式來對比,C++依然會比py快,就像你執行java的class文件,參數指定純解釋執行,不用jit,一樣比py快不少

這裡有個語言設計上的原因,動態性,包括動態類型和代碼動態性,舉個快被我舉爛了的小例子:

for i in xrange(100)

這行代碼的含義,程序員可能認為相當於就是:

for (int i = 0; i &< 100; ++ i)

C++中,如果寫成後者,毫無疑問是速度很快的,但是前者這個py的代碼,卻不能靜態優化為後者,因為當xrange是你所認為的內建的那個xrange,則兩者基本等同(除了int類型實現上的差別),但py的動態性允許運行時改變xrange的值,從而改變這行代碼的含義

甚至對於全文分析的靜態性檢查都可能沒啥作用,可能有人會說找找程序中是否有地方改變xrange的值,但是:

s = raw_input()

exec s

運行時輸入一個xrange=123,編譯器又怎麼能知道呢:)

不過jit可以針對這種代碼做優化,因為可以感知到運行時的一些信息

總之,就是因為py自身設計了(看著炫酷平時又比較少用)的特性,而編譯器為了考慮這些特性,編譯過程會很保守(基本就是直白地翻譯為位元組碼),再加上py的對象機制(基礎類型也是對象),整體速度(比想像中)慢就是正常的了

=============================

恩,補充一下

有人問,那麼我們這樣寫代碼,是不是就規避了xrange問題呢:

i = 0
while i &< 100: ... i += 1

恩,這樣的確會稍微快那麼一點點,但是代碼可讀性。。。而且速度意義其實也不是很大,原因有幾點,譬如說,py的整數計算,實際上是一個優點複雜的過程,會檢查是否溢出,溢出轉為long之類(3.x直接用的long實現,更慢一點)

那有人追問,是不是py如果不考慮溢出自動提升long,也就是跟java之類語言一樣整數計算簡單直接做,溢出就迴環(或者在C中是ub),那是不是就快了呢?恩,這樣是會又快那麼一點點,但是還不夠,因為動態類型,這裡的i在運算時,下層實際上是一個通用的PyObject指針(針對CPython的實現而言),引用的是一個堆上的實際對象,這樣做是為了實現任何變數可以引用任何類型的對象,而對象在運算的時候,實際上是先查找相關函數,然後調用,這樣依然是很慢的

哦,還有人追問,那我給py增加類型聲明,指定i就是一個int類型,是不是就一樣了呢?在同樣的解釋執行下,的確基本一樣了,不過,這跟java等語言還有啥區別啊……


除去GIL, 動態解釋等原因,補充一下:

在通常使用的CPython中,python本身就是c實現的一個庫,python中的一切變數,函數等在C看來都是python對象 - PyObject,具體參考官方文檔中的1. Extending Python with C or C++ 章節~

最後貼一張圖自己體會,python中的for循環 在底層看來比for(int i=0;i&<10;++i)多了多少東西~


這裡邊主要是解釋性語言和編譯性語言的區別。

Python要瞻前顧後維護的事情太多了,為了能安全穩定的解釋運行。

C++操心的事兒就少多了,你讓他幹啥它就悶頭干,你讓他去寫0x00000000內存地址他都敢去干,然後讓程序死給你看。


不貼代碼咋分析……? C++編譯選項是啥?


類型檢查很費時


更新:是我理解有誤,給各位道歉。我答題的時候各個答案還在討論解釋型語言等原因,我認為是一些python特有的特性導致的慢,而不只是一些老生常談的動態語言或解釋型語言導致的,因為就算在動態或者解釋型語言里,python也算慢的。沒有仔細想就答了個GIL,現在想想確實怪不到GIL上。而且當時題主好像沒有更新問題的詳細說明,也有可能是我審題不認真沒有看到問題的詳細說明。

其他大佬答的我是服的,給大佬遞帽。

——————————————原答案——————————————————

其他答案基本都能解釋python比cpp慢,但不能解釋python比cpp慢很多。如果說是因為解釋型語言,因為虛擬機,那麼python為什麼比Java比JavaScript也慢很多呢?

python的慢已經是眾所周知,可以說是「臭名昭著」,最關鍵的一點在與GIL,又稱全局解釋鎖,因為GIL的緣故,使得解釋器沒法上很多優化。Cpp如今為什麼這麼快,還不是靠這麼多年的編譯器優化。當然GIL最終導致大家對CPython在提速這方面已經放棄治療,大佬們都去折騰PyPy了,CPython就這樣吧。不過最近的PyCon上有大佬提了GIL這個事情,感覺可能是要針對這一塊動刀了吧。

當然GIL這個東西功勞是很大的,在易用性方面真是功不可沒。只不過一個單核時代的解決方案已經適應不了這個多核的時代了。


一堆人沒看代碼就判斷,python慢是應該的,但是跟代碼也有關係,你用C寫沒效率的代碼一樣慢過python。多數情況下慢是因為代碼寫的不哈皮。


這個問題下,單純討論語言本身的效率無意義,一定要將測試代碼所使用的環境(編譯器、虛擬機等)描述出來。因為環境不同,同樣代碼的表現差距就很大。

通常情況下,Python 使用 CPython 解釋器,而 C++ 使用 GCC / Clang / VC++ 等編譯器。

那麼這個問題就變成了:

使用同樣演算法和邏輯書寫的Python代碼和C++代碼,分別用 CPython 和 GCC 執行/編譯,在 同樣的機器下,在Windows x64 (Linux/Unix等) 系統下運行——

為什麼 CPython 執行 此Python代碼 比 直接執行 GCC 編譯 此C++代碼 後的程序 效率要低許多?

那麼這個問題的回答就是,因為 CPython 解釋器 執行的 Python 代碼比 直接執行 GCC 編譯後的代碼 多了一層(多層)抽象,這層抽象產生的開銷(解釋開銷),在特定環境(如題主給出的環境)下是不容忽視的。

至於這個開銷怎麼節省,可以通過:

1. 將Python代碼編譯為位元組碼並存儲(編譯為.pyc或者.pyo),在以後的運行(這次運行結束以後)時不會從源代碼重新編譯為位元組碼;(節省從源代碼到位元組碼的開銷)

2. 使用 PyPy 等 使用 JIT 技術的 Python 實現。JIT 技術可以使程序在運行時轉換為機器碼,直接執行機器碼比每次都從位元組碼解釋執行要省時省力。(節省從位元組碼解釋執行的開銷)

請注意,一門語言可以實現為解釋的,也可以實現為編譯的,直接討論語言的類型,解釋型和編譯型只是指在通常情況下人們對這門語言實現方式的默認看法。討論語言效率必須要與語言的實現相聯繫。


你覺得是看一本中譯本的書快還是聽別人一句一句翻譯給你聽快呢?


簡單的說,性能就是計算機世界的貨幣,用於支付用戶體驗的。Python比C++好學好寫又方便那麼多,這些可不是免費的。

另外編譯器/解釋器優化方面也是很影響速度的。


這事你得先貼代碼,python雖然慢.但還是得看怎麼寫.

有人出了代碼.借用一下.

想要加速很容易.你只需要一個叫Cython的神器.

cdef long calc_q(long n, long i=0, long cols=0, long diags=0, long trans=0):
cdef long j;
cdef long col;
cdef long diag;
cdef long tran;
cdef long rt;
if i == n:
return 1
else:
rt = 0
for j in range(n):
col = 1 &<&< j diag = 1 &<&< (i - j + n - 1) tran = 1 &<&< (i + j) if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: rt += calc_q(n, i + 1, cols | col, diags | diag, trans | tran) return rt def calc( n, i=0, cols=0, diags=0, trans=0): return calc_q(n,i,cols,diags,trans)

生成的代碼如下

/* "temp.pyx":13
* return 1
* else:
* rt = 0 # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * for j in range(n): * col = 1 &<&< j */ /*else*/ { __pyx_v_rt = 0; /* "temp.pyx":14 * else: * rt = 0 * for j in range(n): # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * col = 1 &<&< j * diag = 1 &<&< (i - j + n - 1) */ __pyx_t_2 = __pyx_v_n; for (__pyx_t_3 = 0; __pyx_t_3 &< __pyx_t_2; __pyx_t_3+=1) { __pyx_v_j = __pyx_t_3; /* "temp.pyx":15 * rt = 0 * for j in range(n): * col = 1 &<&< j # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * diag = 1 &<&< (i - j + n - 1) * tran = 1 &<&< (i + j) */ __pyx_v_col = (1 &<&< __pyx_v_j); /* "temp.pyx":16 * for j in range(n): * col = 1 &<&< j * diag = 1 &<&< (i - j + n - 1) # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * tran = 1 &<&< (i + j) * if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: */ __pyx_v_diag = (1 &<&< (((__pyx_v_i - __pyx_v_j) + __pyx_v_n) - 1)); /* "temp.pyx":17 * col = 1 &<&< j * diag = 1 &<&< (i - j + n - 1) * tran = 1 &<&< (i + j) # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: * rt += calc_q(n, i + 1, cols | col, diags | diag, trans | tran) */ __pyx_v_tran = (1 &<&< (__pyx_v_i + __pyx_v_j)); /* "temp.pyx":18 * diag = 1 &<&< (i - j + n - 1) * tran = 1 &<&< (i + j) * if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * rt += calc_q(n, i + 1, cols | col, diags | diag, trans | tran) * return rt */ __pyx_t_4 = (((__pyx_v_col __pyx_v_cols) == 0) != 0); if (__pyx_t_4) { } else { __pyx_t_1 = __pyx_t_4; goto __pyx_L7_bool_binop_done; } __pyx_t_4 = (((__pyx_v_diag __pyx_v_diags) == 0) != 0); if (__pyx_t_4) { } else { __pyx_t_1 = __pyx_t_4; goto __pyx_L7_bool_binop_done; } __pyx_t_4 = (((__pyx_v_tran __pyx_v_trans) == 0) != 0); __pyx_t_1 = __pyx_t_4; __pyx_L7_bool_binop_done:; if (__pyx_t_1) { /* "temp.pyx":19 * tran = 1 &<&< (i + j) * if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: * rt += calc_q(n, i + 1, cols | col, diags | diag, trans | tran) # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * return rt * */ __pyx_t_6.__pyx_n = 4; __pyx_t_6.i = (__pyx_v_i + 1); __pyx_t_6.cols = (__pyx_v_cols | __pyx_v_col); __pyx_t_6.diags = (__pyx_v_diags | __pyx_v_diag); __pyx_t_6.trans = (__pyx_v_trans | __pyx_v_tran); __pyx_t_5 = __pyx_f_4temp_calc_q(__pyx_v_n, __pyx_t_6); __pyx_v_rt = (__pyx_v_rt + __pyx_t_5); /* "temp.pyx":18 * diag = 1 &<&< (i - j + n - 1) * tran = 1 &<&< (i + j) * if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: # &<&<&<&<&<&<&<&<&<&<&<&<&<&< * rt += calc_q(n, i + 1, cols | col, diags | diag, trans | tran) * return rt */ } }

可以看到和手寫的c代碼沒有太多區別.


c++你用的什麼編譯器?

怎麼說呢,c++是c的超集,本質上,它還是c,而c已經發展了40多年了,光編譯器就十個指頭都數不過來。你明白吧,業界已經吃透c了,它經過了高度的優化,不管是軟體還是硬體都對c進行了優化,並且,現在流行的C C++編譯器甚至都是用c C++本身寫的,它已經實現了自舉。

而python就不一樣了,首先像python這樣的運行在虛擬機上的語言就不像c c++那樣直接編譯為二進位,就是機器碼,它們首先是編譯為這門語言的解釋器內部的位元組碼,位元組碼還要經過虛擬機的指令轉換才能進入彙編階段,其次它們也沒實現自舉。最終都是要通過虛擬機執行才行。這中間多了很多步驟,並且,python的虛擬機並沒有做優化。是奔著夠用就行的原則。

運行在虛擬機上的語言有很多,比如java javascript python c# php等。幾乎高級語言,越接近自然語言的都運行在虛擬機上。它們都有對應的解釋器,就是虛擬機。javascript性能高是因為有人為它優化,前幾年經過了數次的瀏覽器大戰的結果,戰爭促使科技進步,這話不是蓋的。

c c++更像是中間語言,既不高級也不低級,想要python這類虛擬機語言快,就得讓它實現自舉,這是一個思路。也就是源代碼直接轉換為二進位機器碼,但現在沒有懂優化懂編譯原理的人為其實現。像javascript的源代碼是直接通過v8 轉換為機器碼執行的。它本質上和c c++一樣通過編譯器轉換為機器碼是一樣的效率。中間省了位元組碼和虛擬機指令等步驟,效率自然高速度自然快了。

所以,想要性能又想方便簡單容易使用就轉javascript吧,python就適合簡單分塊的編程,所以它的模塊巨多,你只要組合起來就行,就像樂高一樣,它的設計哲學更像freebSD,設計經久耐用的簡單模塊(而不是複雜的軟體),什麼時候要用就拿來用,永不過時。而不像其他的,要建立軟體,什麼都得從頭來,從零開始設計。python並不適合建立巨大的軟體工程項目,它更適合分散式。c c++是真的既難學又難用,還不如用彙編去,那速度更快一個等級。c c++就像絕情谷 公孫止的武功一樣,難練易破。

///

更新:

可能我的用詞不嚴謹,並且有很多地方產生了歧義,導致很多知友反對和嘲諷我,不過不要緊,我覺得這是鞭策,我會虛心請教。


這個場景跟gil 沒有半毛錢關係。。。

C加加是編譯後執行,cpython 是一天天的位元組碼解釋編譯,且沒有編譯緩存這些指令。

Pypy實現了jit ,但對於c寫的庫支持力度不行


幾乎全部的編程語言都不如C++/C。

不是為什麼python比C++慢,是為什麼C++怎麼這麼快


@Coldwings 貼了numba 和 pypy的,我來貼cython版的優化。

硬體環境: 機器 i7-6700 + virtualbox ubuntu 14.04 虛擬機,CPython 3.6.1,cython 0.27.2, 0.8秒, n = 13

需要裝 cython, 在pyx模式下代碼:

def calc(long n, long i=0, long cols=0, long diags=0, long trans=0):
cdef long rt
cdef int j
cdef long col
cdef long diag
cdef long tran
if i == n:
return 1
else:
rt = 0
for j in range(n):
col = 1 &<&< j diag = 1 &<&< (i - j + n - 1) tran = 1 &<&< (i + j) if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: rt += calc(n, i+1, cols | col, diags | diag, trans | tran) return rt

cython 的Pure python mode: 0.3秒

pxd:

cpdef long calc(long n, long i=*, long cols=*, long diags=*, long trans=*)

py:

pure python mode中,函數內變數需要用 cython.locals 裝飾器,進行靜態聲明,不然依然是 python object 會影響性能。

import cython
@cython.locals(rt=cython.long, j=cython.int, col=cython.long, diag=cython.long, tran=cython.long)
def calc(n, i=0, cols=0, diags=0, trans=0):
if i == n:
return 1
else:
rt = 0
for j in range(n):
col = 1 &<&< j diag = 1 &<&< (i - j + n - 1) tran = 1 &<&< (i + j) if (col cols) == 0 and (diag diags) == 0 and (tran trans) == 0: rt += calc(n, i+1, cols | col, diags | diag, trans | tran) return rt

兩種模式 通用的 setup.py(需要修改 py和 pyx後綴),執行 python setup.py build_ext

from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules = cythonize(
"QueenN.py", # our Cython source
))


一個編譯 一個解釋。。。編譯肯定比解釋快的多呀。。。


「Life is short, you need Python!」——Bruce Eckel

人家Python的目標本來就是快速開發,節省開發者時間


推薦閱讀:

機器學習演算法中GBDT與Adaboost的區別與聯繫是什麼?
對於ACM或者OI的問題中,有哪些方法判斷一道題是使用了什麼演算法呢?
在浮點數組中最快的選擇最大k個數的演算法是什麼?
次優查找樹的原理是什麼?
RSA的公鑰和私鑰到底哪個才是用來加密和哪個用來解密?

TAG:Python | 演算法 | 編程 | C | 編譯 |