1. 初衷
最近學用python,python不愧是為程序員考慮的編程語言,寫起來很快很方便,大大節省開發效率。而且,對於小規模程序,運行效率也不錯。前兩天寫了一篇博文《【總結】學用python寫程序》,大大地誇獎了python一番。不過這兩天,我就受到“詛咒”了。數據規模稍微大一點,python的執行效率的差勁就體現出來了。這兩天寫的一個程序,盡管在我所知道的范圍內,我做了python語言能做的優化,不過程序依然運行了五個小時之久。想把程序改成c++的,不過開發時間較長,而且未來可能還有改動。所以暫罷。
上網上查了查python效率的問題。一方面,網上這方面資料不是很多,例如:我們都知道stl裡面set是用紅黑樹實現的,不過python的set怎麼實現的,貌似網上沒有。這說明用python的人貌似都不關心效率問題。另一方面,據網上資料說,python運行效率比java還慢。我作為c++程序員從前很鄙視java的運行效率,原來python還不如java呢!不過java是虛擬機,python是解釋器,為什麼python更慢呢?原因在於python更加“面向對象”,python的所有類型都是對象,連最普通的整數變量都是對象,都要在運行的時候才能夠確定類型、才能夠動態創建......這大大加重了運行時的負擔,所以運行效率才這麼差。對比之下,同樣的程序用cython寫,僅僅是聲明了變量類型,運行效率就會有35%的提升。
我從前用過openmp,見從前的博文《簡單嘗試windows多線程程序》。感覺openmp是神器一個,既方便寫程序,又能利用cpu的多個核心,大大提升運行效率。問題是,python中能夠使用openmp麼?答案是悲觀的。python的默認實現是cpython,也就是用c來做的實現,而c的函數大部分都不是線程安全的,為了利用這些函數實現、同時又為了運行時的線程安全,python做了GIL(Global Interpreter Lock)的限制,也就是說,同一段時間內只有一個線程才能夠訪問python解釋器。不過這也使得python上面的並發特別困難。
不過也不是一點方法都沒有,cython現在已經支持了openmp。cython是什麼?和python、cpython什麼關系?python是腳本語言,cpython是用c來實現的python的解釋器,cython是另外一種編程語言,介於python和c之間。實際上cython的設計初衷也是這樣,既要利用python快捷的編程速度,又要有c語言的運行效率。cython和python的一個顯著區別就是,cython的所有變量都要明確聲明變量類型——僅僅這一點,相同的程序,cython的運行效率就要比python的高35%!雖然cython是一種獨立的編程語言,不過貌似大家不用他獨立的編寫程序,而是用它來編寫python的c擴展(用c高效實現某些程序,再給python調用)。這幾天嘗試的,就是在cython上面用openmp,並寫成python的c擴展,給python調用。
2. 環境
windows7 + 32bit + vistual studio 2008 + python 2.7.3 + eric4,都是默認安裝路徑。
3. 安裝cython
官網上下載的Cython-0.20.1,從控制台上切到cython的路徑,運行setup.py就一路編譯安裝下去了,沒遇到其他問題。
在網上看到,很多人在安裝的時候遇到很多問題,基本上都是找不到c++編譯器,具體表現是提示找不到一個叫“vs....bat”的文件。解決辦法通常是安裝mingw(gcc在windows下的版本),然後修改一個.cfg文件,指定用這個編譯器來build。
我的安裝過程沒有遇到問題,看網上的解釋,貌似是python2.7的cpython是用vistual studio 2008來編譯的,默認找對了編譯器,所以沒問題了。總之,裝上了,沒問題。
4. 寫pyx文件
pyx文件是python的c擴展文件,代碼要符合cython的規范,用什麼編輯器寫都行。我在eric4上寫的,結果它默認用python解釋器來進行解釋,還提示有bug,“語法錯誤”。不理會他,本來cython的語法在python裡面就不支持。創建TestOMP.pyx文件,並在文件中寫代碼如下:
from cython.parallel import prange, parallel, threadid from libc.stdio cimport printf def Test(): cdef int i = 0 cdef int sum = 0 for i in prange(1000000, num_threads=2, nogil=True): printf ("%d\n", i)
5. 寫setup.py文件
上面的pyx文件還僅僅是源代碼文件,要想被python調用、要想運行,僅僅寫了源代碼還是不夠的。具體來說,還要轉成.c或者.c++的文件,並且再進一步轉成.pyd文件。pyd文件才是可以直接使用的文件。為了達到上述目的,就要寫一個setup.py腳本,如下:
#!/usr/bin/python #python version: 2.7.3 #Filename: SetupTestOMP.py # Run as: # python setup.py build_ext --inplace import sys sys.path.insert(0, "..") from distutils.core import setup from distutils.extension import Extension from Cython.Build import cythonize from Cython.Distutils import build_ext # ext_module = cythonize("TestOMP.pyx") ext_module = Extension( "TestOMP", ["TestOMP.pyx"], extra_compile_args=["/openmp"], extra_link_args=["/openmp"], ) setup( cmdclass = {'build_ext': build_ext}, ext_modules = [ext_module], )
6. 寫TestOMP.py文件
上述兩個步驟,相當於把某個python效率瓶頸模塊(這之前需要用profile工具來定位)用效率更高的代碼寫成了python的c擴展形式,接下來,就是要在python代碼中調用他們。TestOMP.py就是這個調用的腳本,如下:
from TestOMP import Test Test()
7. 結果
507569 507570 507571 507572 507573 507574 507575 507576 507577 507578 507579 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216
8. 對應的c++程序
同時寫了一個對等的c++程序,如下:
#includeusing namespace std; void PlayOMP (void) { #pragma omp parallel for num_threads(2) for (int i=0; i<1000000; i++) cout <
同時,在Configuration Properties->C/C++->Language->OpenMP Support,在下拉菜單裡選擇Yes。並且從C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.OPENMP 和 C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugOpenMP目錄下分別拷貝vcomp90d.dll和vcomp90.dll文件到工程文件當前目錄下,或者將上述兩個路徑設置到環境變量裡面。編譯、運行,結果和python上面的一樣——不過貌似更快!
完。
轉載請注明出處:http://blog.csdn.net/xceman1997/article/details/26977483