程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 嘗試cython和openmp

嘗試cython和openmp

編輯:C++入門知識

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)

第一句引入了cython中的並行處理模塊,尤其是prange。我理解,prange就是“python 'range' of parallel version”,就是並行循環。第二句是引入了c語言中的‘printf’函數。整個文件就定義了一個Test函數。看到,每個變量在使用前都要聲明類型。在prange中,有參數‘num_threads’來設定並發數量。nogil表示‘no gil(Global Interpreter Lock)’,想要獲得並行,這個參數就要設置。在循環過程中,調用了c的庫函數printf,來打印每個整數值。


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], 
) 

這個完全是一個python腳本,可以在python解釋器下面運行。在控制台下,運行如下命令‘python setup.py build_ext --inplace’,就生成了TestOMP.pyd文件。當然,同時還有一些雜七雜八的文件,如‘build’目錄下面的‘lib’文件。這都提示著,這是在windows vistual studio環境下。在linux+gcc環境下,就要生成.so文件了,而且“/openmp”的選項就要寫成“-fopenmp”


6. 寫TestOMP.py文件

上述兩個步驟,相當於把某個python效率瓶頸模塊(這之前需要用profile工具來定位)用效率更高的代碼寫成了python的c擴展形式,接下來,就是要在python代碼中調用他們。TestOMP.py就是這個調用的腳本,如下:

from TestOMP import Test

Test()

這個就很容易了,import並且調用。在控制台下,輸入“python TestOMP.py”,運行。


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

上面是在控制台上的輸出的一個片段。能夠看到,的確是prange把1000000這一個大循環分成了兩個區間:[0, 500000) 和 (500001,1000000],兩個循環並行運行,並交替使用控制台IO進行輸出。


8. 對應的c++程序

同時寫了一個對等的c++程序,如下:

#include 

using 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


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved