專家課堂 | 人生苦短,我用Cython!論用Cython加速Python並行計算的實踐

2021-03-02 聯通數科

大神Bruce Eckel曾說:Life is short, you need Python! 隨後,Python創始人Guido就將印有「人生苦短,我用Python」的文化衫穿上身。

經過兩位大師的調侃式互動,加之自身較為強大的腳本語言優勢,Python近些年在大數據開發的常用高級語言中備受歡迎,大有超越Java的態勢;尤其在數據科學、深度學習領域,Python是當仁不二的選擇。

不過,當模型較複雜、運算量較大時,Python難免面臨運算速度慢的尷尬。那麼,選擇Python就必須要忍受它的性能短板嗎?

試試Cython吧!

Cython結合了Python的易用性和原生代碼的速度,可以說是便捷性與高性能的完美融合。

接下來,就請本期大數據專家——凌逍跟大家分享他使用Cython加速Python計算的實踐經驗。

提到Python的並行計算,不能不提極富爭議性的GIL(global interpreter lock),全局解釋器鎖。為了利用多核CPU的計算資源,Python並行只能使用進程,如multiprocessing庫等。如果用其他程式語言來寫,那麼開發者只需用把同步鎖就可以把線程之間的通信過程協調好,而在Python中,我們卻必須使用開銷較高的multiprocessing模塊。 multiprocessing的開銷之所以比較大,原因就在於: 主進程和子進程之間,必須進行序列化和反序列化操作。對於某些較為獨立,且數據量較小的任務來說,這套方案非常合適。所謂獨立,是指待運行的函數不需要與程序中的其他部分共享狀態。數據量較小是指主進程與子進程之間傳遞的數據比較小。如果待執行的運算不符合上述特徵,那麼multiprocessing所產生的開銷,可能無法通過並行化來提升程序速度。在那種情況下,也可以利用multiprocessing所提供的些高級機制,如共享內存等。不過,這些特性用起來非常複雜而且效率很低,而且會有內存佔用過多的問題。

下面通過一個簡單例子來說明:

字典nodeDict中存放的是編碼後的基站(key)與其他有切換關係的基站的列表(value)。我們想用與某個基站有切換關係的其他基站的經緯度來反向評估當前基站經緯度的異常。用最簡單的方法,計算切換基站列表的平均經緯度和當前基站的距離。距離越大則表示當前基站定位可能出現異常。對於這種簡單而量大計算,multiprocessing多進程加速的效率很低。

如下圖所示:

可見大部分時間都浪費在進程之間的通信上和序列反序列化上,多進程的效率比單進程的還低。對於這種簡單且量特別大的計算任務,多進程並行並不能加速任務。那麼在Python的生態圈內有沒有能使用線程來加速並行的神器呢?答案是有的,Cython從0.15版本開始就實現對原生並行編程的支持,使用OpenMP,讓Pythoner可以輕鬆的繞過GIL的限制。

Cython可以直接理解為一種非常類似Python的語言,實際上Cython是一種部分包含C語言,以及完全包含Pyhton語言的一個超集。Cython和Python的一個顯著區別就是,Cython的所有變量都可以明確聲明變量類型。相同的Python程序僅僅是添加了類型聲明就能在Cython中獲得近50%的速度提升。

但真正讓我們激動的是在Cython中可以無視GIL的存在而盡情使用線程加速,先來個簡單演示:

對變量x做累加,看起來我們得到了一個非常錯誤的答案,並且每次運行代碼時都會得到不同的答案,因為對x的寫操作不是安全的。這就是需要鎖的地方,在這種情況下,線程可以獲取x上的鎖,更新它,然後釋放鎖。這意味著一次只有一個線程在x上運行。將代碼更改如下:

這樣就能得到正確的結果。我們使用Cython來解決上面的問題,但不能再使用Python中的字典和列表,因為Python中的變量都自動帶了鎖(GIL)。還好Cython已經封裝了C++標準庫中的容器:deque,list,map,pair,queue,set,stack,vector。完全可以替代Python的dict, list, set等。

相互類型轉化規則如下:

下面的函數將上述的基站切換字典轉化成為了map類型,同理,計算距離的函數同樣需要改為nogil形式

Cython中有三種定義函數的方式,def, cdef以及cpdef。其中def對應為Python函數:如在Python中定義函數一樣,以Python對象作為參數,返回Python對象。cdef為C函數,接受Python對象或C值作為參數,並且可以返回Python對象或C值,cdef函數不能直接在Python中調用。cpdef為混合函數,cpdef可以從任何地方調用,但是當從其他Cython代碼調用時使用更快的C調用。即使從Cython調用,cpdef也可以被子類或實例屬性上的Python方法覆蓋。

如果發生這種情況,大多數性能優勢會丟失。用Cython我們可以方便的向C代碼傳遞和返回結果,Cython會自動為我們做相應的類型轉化。

下面是我們定義的計算函數:

因為我們此次的數據量較小(約20萬),為了展現並行計算的加速,我們僅僅對中間的計算部分計時,並且沒有對結果進行收集。可以看到僅僅是單線程,Cython就將計算時間降至0.08秒左右,比Python版的程序速度提高了一百多倍。而且即使有更大的數據量,我們還可以開啟多線程加速,加速效率接近於線性加速。

Cython通過對Python代碼增加類型聲明和直接調用C/C++函數,使得從Python代碼中轉成等價的C/C++代碼的效率大大提高,而且它幾乎支持全部Python特性,可以說任何Python代碼都是有效的Cython代碼,這使得引入Cython技術的成本降到了最低。目前很多Python庫都使用Cython來提高效率,如pandas, scikit-learn, PyYAML等等。

玩轉Hadoop千節點Docker部署(上篇)

________________________________________

編輯:胡瑞

審核:凌逍、李光

相關焦點

  • Python代碼加速利器:Cython
    讓我們用斐波那契數列來證明這一點。%%cythoncdef extern from "math.h":cpdef double sin(double x)Cython還有許多其他功能,例如並行。結論如果您有時遇到不得不等待太長時間才能執行Python代碼的問題,Cython提供了一種非常整齊且高效的方法來加速您的代碼。最重要的是,如果您對C語言稍微熟悉,它提供了許多進一步優化代碼的功能。我絕對建議您仔細查看文檔。
  • 如何壓榨Cython及OpenMP優化Target Encoding
    最重要的一點就是我們需要先優化python代碼(算法複雜度),再去用cython(底層),最後才用並行(多線程多進程),不能本末倒置。這個時候,我覺得python代碼在我能力範圍內我覺得優化到不錯了,我想,是時候進行底層優化了(Cython)
  • cython初體驗
    本文是筆者第一次使用cython的一個小結筆者最近參與了一個項目,其目的是提升一個python程序的運行速度。其中一個手段就是利用cython來優化原來的python代碼。筆者之前沒有接觸過cython,所以這次屬於在實踐中學習新知識。
  • 10分鐘入門Cython
    www.zhihu.com/people/li_xiaowen/activities作者其他好文傳送門:協同過濾(ALS)的原理及Python實現回歸樹的原理及Python實現本文github:https://github.com/tushushu/flying-python
  • 高性能Python:使用Cython
    編程派微信號:codingpy準確的說,Cython是單獨的一門語言,專門用來寫在Python裡面import用的擴展庫。實際上Cython的語法基本上跟Python一致,而Cython有專門的「編譯器」;先將 Cython代碼轉變成C(自動加入了一大堆的C-Python API),然後使用C編譯器編譯出最終的Python可調用的模塊。
  • 這個加速包讓Python代碼飛起來
    可以通過C語言或者Python代碼使用該函數 有了對Cython的了解,我們可以更進一步,開始加速我們的代碼了! 使用Cython提高代碼的運行速度 首先,我們建立一個Python代碼的基準——使用一個for循環來計算某個數的階乘。
  • 量化交易之《Python數據分析》
    Pony ORM用純python編寫的ORM,能自動進行查詢優化,提供了一個圖形用戶界面的資料庫模式編輯器,支持自動事務處理,自動緩存,組合關鍵字。然後,計算數據點之間的歐式距離,以此構建一個矩陣。程序實際上充當了python和C++之間的膠水,cython工具,可以把Python代碼編譯成接近機器語言的代碼。
  • Cython 0.20 發布,Python 的 C 語言擴展
    Cython是單獨的一門語言,專門用來寫在Python裡面import用的擴展庫。
  • Python 性能優化
    不同的語言會有不同的側重,python語言毫無疑問更在乎編碼效率,life is short,we use python。雖然使用python的編程人員都應該接受其運行效率低的事實,但python在越多越來的領域都有廣泛應用,比如科學計算 、web伺服器等。程式設計師當然也希望python能夠運算得更快,希望python可以更強大。
  • 快100 倍,Python 為自然語言處理加速度!
    利用 spaCy 和一點點 Cython 給 NLP 加速。自去年發布 Python 的指代消解包(coreference resolution package)之後,很多用戶開始用它來構建許多應用程式,而這些應用與我們最初的對話應用完全不同。
  • Cython 助力 Python NLP 實現百倍加速
    在去年我們發布了用 Python 實現的基於神經網絡的相互引用解析包(Neural coreference resolution package)之後,在社區中獲得了驚人數量的反饋,許多人開始將該解析包用到各種各樣的應用中,有一些應用場景甚至已經超出了我們原本設計的對話框用例(Dialog use-case)。
  • 快 100 倍,Python 為自然語言處理加速度!
    在本文中我想分享一些在這個項目中學習到的經驗,具體來說包括:所以其實這裡有點耍花招,雖然我們是在討論 Python,但還要用一些 Cython的魔法。但別忘了,Cython 是 Python 的超集(http://cython.org/),所以別被它嚇住了!你現在的 Python 程序已經是 Cython 程序了。
  • 開課吧:「人生苦短,請用python!」給你這幾大理由!
    「人生苦短,請用python!」Python基礎作為一個學習過程式語言的過來人,如果是自己自學編程,我推薦學Python。說說我看到的原因:1、python語法更簡潔,十分舒適基本上我上面列舉的所有程式語言中,python的代碼是最簡潔的看起來,很適合自學,容易理解2、python已經走入中小學課堂,入門難度低有省市已經將
  • 如何讓Python腳本變成Windows應用
    用pyinstaller,打出來的文件巨大無比而且報錯。以前在linux系統下有過批量py文件為so文件的經驗,那在windows下,可能也有類似的方法去解決問題。學習網上資料後,準備用cython把py文件編譯成c文件,然後再用visual stuido裡的cl編譯成exe文件的方法。
  • 簡明 Python 教程:人生苦短,快用Python
    Python經久不衰、近些年更是大放異彩的原因是因為設計者一直秉持 「優雅」、「明確」、「簡單」 的哲學,用近乎偏執的態度貫穿其中。如果你在Python的編輯器或者命令行中輸入 Import this ,你會看到如下的宣言。
  • 如何讓Python像Julia一樣快地運行
    但是,我想到的第一個問題有所不同:Julia 團隊能否以最適合 Python 的方式編寫 Python 基準測試? 我對這種跨語言比較的觀點是,應該根據要執行的任務來定義基準測試,然後由語言專家編寫執行這些任務的最佳代碼。如果代碼全由一個語言團隊編寫,則存在其他語言未得到最佳使用的風險。
  • Python跳槽薪資報告:人生苦短,Python工程師們還好嗎?
    Python跳槽薪資報告:人生苦短,Python工程師們還好嗎?  「人生苦短,快用python」,這話曾是不少選擇投入Python麾下的「碼農」們的一句調侃和自我標榜。  與敏捷開發、大數據甚至人工智慧風口捆綁在一起的Python,似乎從來不缺熱度。2019年1月TIOBE排行榜顯示,由於全球流行度在過去一年中漲幅最高(3.62%),Python一舉獲得了2018年度程式語言的稱號。
  • 快速掌握用python寫並行程序,乾貨滿滿
    目錄2.2 改用GPU處理計算密集型程序三、用python寫並行程序四、multiprocessing實戰小子今天想來談談「並行計算」,作為一個非科班人員,我為什麼去搗鼓這麼一個在科班裡也比較專業的問題了
  • Python · numba 的基本應用
    雖然我用 Python 也有那麼兩年左右了,但也只能模模糊糊地感受到這麼兩點:* Python 太動態了* 如果能事先編譯一下 Python,讓它靜態一點,速度應該就會上來於是我們就有了 cython。然而 cython 畢竟不是原生的 Python 代碼,使用起來還是有諸多不便的。為此,numba 就成了一個功能強大又容易上手的替代選擇。