當前位置:
首頁 > 最新 > 加速python科學計算的方法(一)

加速python科學計算的方法(一)

2017/12/13

Wednesday

開題答辯結束後的第一天。走在校園的時候,前面幾個女生的手機里突然開始放陳慧嫻的《千千闕歌》,我一下子思維就愣著了,步伐也慢了下來。沒想到現在的90後也有喜歡老的粵語歌啊,真有種相見恨晚的感覺。就在我準備加速行走看看她們到底是誰時,她們卻開始大聲討論起來:你程序這裡寫的這個指針這麼調用返回回來的結構體地址blablabla。。。意境全無了,我還是轉彎去買橘子吧。

硬切。最近大家抬頭低頭都在說程序、模型等等的,作為本公眾號Stage2的第一篇推文,那我們今天還是說說程序的事吧。根據IEEE Spectum 2017發布的編程語言排名報告,Python依靠近期興起的機器學習、深度學習和強化學習等方向的帶動,成為了今年最受歡迎的語言。甚至在最近還有要求小學生開始以python作為編程語言的學習(講道理我認為不應該這樣做,不識英文和數學的編程有意義嗎?還不如學Scratch或者易語言),以及2018年國家計算機等級考試中也增加了python的測試。

即使這樣,很多老牌編程語言依然對python的執行效率冷嘲熱諷。。。

因為原生的python運行得真的是太慢了!!究其原因,最最明顯的一個問題就是python有全局鎖(GIL)概念。GIL是為了應對某些特殊場景而設置的,但是其副作用也非常大。GIL的設置非常複雜,簡而言之可以認為:python在運行過程中無法充分利用CPU的全部計算力,基本上可以認為只用了一個核心的計算能力。這個問題在面對處理大量數據時則更為明顯。

具體而來,我們在用Python處理大(小)數據時最常用的應該就是Numpy和pandas庫了。其中,Numpy釋放了GIL鎖,可以較為充分的利用CPU計算能力,從而達到較高的處理速度。Pandas就不想說了,速度慢的特點依然存在。除了使用Numpy內置的計算代碼外,那我們能不能在某些場景、基本不改變原有代碼的前提下使Numpy的運算速度更進一步地提高呢?提高10倍、100倍、甚至是1000倍?答案是可以的

但是在講這個提升方法之前,有一個概念需要提出來,那就是計算能力。曾幾何時(大約在大三),我還是一個專註於三維渲染的少年,那時候每天執著於三維渲染器(Renderer)的渲染效率研究上。圖像渲染和程序計算是一樣的,都是吃電腦的計算能力。電腦的計算能力主要分為兩個陣營,一個是CPU主導,一個是顯卡(GPU)主導。對於求解複雜問題,CPU具有無可比擬的優勢;但是對於求解簡單問題,GPU可以超越CPU千百倍。

可以這麼做個比喻:CPU好比是10輛大型卡車,每輛車載重10T;GPU則是2000輛小型麵包車,每輛車載重1T。現在有一堆貨物(計算任務)需要從A地運往B地。雖然CPU都是大卡車,組織方便,但是數量少;GPU則完全依靠數量上的優勢來完成貨物運輸,當且僅當這些貨物是可以拆分的(大量的簡單任務)。當運輸任務滿足GPU的拆分條件的話,GPU就可以全面領先CPU了(2000*1>10*10)。

因此,對於原生依靠CPU計算的python及其常用庫Numpy,能不能切換到使用GPU來運行程序呢?

運氣好,有個庫可以幫到我們,那就是Numba.

Numba是一款可以把原生的Numpy代碼遷移至GPU上運行的庫,而且一般情況下,僅僅需要添加一行代碼:@numba.jit,像這樣的裝飾器(decorator)就可以了。這是一種比較懶的寫法(Lazy),沒有對其進行任何指定。Jit指的是Just-in-Time。但是使用它之前,請確保您電腦顯卡是支持英偉達CUDA運算的。

裝飾器就是函數的函數。因為在python環境下一切皆為對象,因此函數也是對象,可以作為其他函數的傳參。對於詳細的裝飾器等內容,推薦閱讀清華大學出版的《python高級編程》,這本書有整整一章內容對此進行了非常深入的探討,這裡就不繼續展開這方面的描述了。

回到Numba的描述中來。對一個將要施加於Numpy對象的函數施加@numba.jit後,numba將自動對其代碼進行速度優化,速度上可以比擬C語言的運行速度。這裡要特別注意一點,該函數體中一定不能出現Numpy庫中的方法,即只能使用純python代碼才會有效果。

比如我們寫以下函數做為示範:

該函數的作用即是通過二重循環來計算一個矩陣元素平方的和(element-wise)。這個是沒有直接的numpy內置函數的。

參賽的選手有兩位,一個是帶有jit裝飾器的函數func1(),另一個則是純python風格的func2()。作用的矩陣是一個1000*1000的大矩陣。

試驗平台如下:CPU i7 7700k,顯卡是GTX1080,內存32G。使用在ipython下使用magic語法來看看兩個函數分別所用的時間:

可以看出,當電腦配置的GPU比CPU有優勢時,切換至GPU來運行程序,速度上會有巨大的提升。對於該例子,numba的運算1個loop大概需要938us,而func2()的一個loop需要433ms=433000us。因此,func1函數的速度比func2幾乎提升了460倍,而且僅僅是多了一行代碼而已!我們在此基礎上再加個對照,即利用Numpy內置的函數:

速度依然比numba修飾過的func1函數慢了幾乎3倍。

但是,numba也不是每次都能提高運算速度的,甚至還會拖慢運行速度。那麼,在什麼時候可以利用numba呢?一般而言,在矩陣中,對於輕量級的、獨立的元素級運算(相當於對Numpy進行Map運算),numba都可以利用GPU>CPU能力的優勢對程序進行提速。此外,numba還可用於pandas的加速運算。一個簡單的切換辦法即是對pandas的Series對象進行.values轉換成numpy的array對象,然後再@numba.jit。我親測非常好用,把一個需要40來分鐘的計算任務縮短至1分鐘,效果明顯。對於numba加速pandas的方式詳情可查看pandas官方API。看API是最好的學習辦法。

作為拋磚引玉,本文僅僅是對numba進行了非常淺顯的展示。真正的numba遠遠不僅僅如此,還可以設置parallel、multi-thread、cache等等方式繼續加速計算。感興趣的朋友可查看numba官方API 來了解細節:http://numba.pydata.org/numba-doc/dev/index.html 。話說回來,提升python運行效率的當然不僅僅有numba,還有許多策略,比如多線程、多進程、Cpython、Pypy 的jit等等技術,這些在以後的推文里會進行介紹。

本公眾號終於有原創聲明留言功能了!!!歡迎各位朋友一起交流~~

GIF/1K


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 全球大搜羅 的精彩文章:

汪星人的臉被鏟屎官捏,吃瓜群眾不厚道的樂了
金絲玉被人所詬病的一大硬傷
新婚最關鍵的時刻,新娘子被人偷走了!
印度新型飛機即將服役,領先中國同類飛機將近60年
婚後生活┋男人們的臭毛病

TAG:全球大搜羅 |