最近一直在做python工程化相關的工作,頗有心得,遂總結一下。
一是為了整理思緒,二是為了解放自己健忘的大腦。
python是一個C的語法糖盒子
原生的python通常都是由cpython實現,而cpython的運行效率,確實讓人不敢恭維,比較好的解決方案有cython、numba、pypy等等
cython
是目前我認為發展最好,最靠譜的一項Python加速解決方案。
使用cython編譯過后的代碼,通常會對原python代碼有2倍以上的速度提升。cython的編譯也很簡單,只需要構建一個setup.py,然后執行:
python setup.py build_ext
numba
numba也是我比較看好的,它的亮點在於使用裝飾器的方式應用jit技術,例如下面的代碼:
@jit def run_xxx(): ...
可直接將run_xxx方法進行高效的c編譯。
但在大多數應用場景下(尤其是采取了服務拆分或微服務的架構策略),這種功能反而讓人有種雞肋的感覺
只能說numba更適用於模型開發的場景,在模型應用和部署的環節,numba的作用很尷尬
pypy
pypy相對比較小眾,這是由於它本身的限制條件較多,尤其是對python第三方包的支持上面更是非常局限。由於我在做python開發的過程中,經常需要限制版本,以及引入較多的第三方包,所以pypy就不在考慮的范圍內了
不要輕易相信聲稱自己很快的模塊和方法
曾經在網上看到有人發文,聲稱numpy是目前python下非常高效的一個模塊,而numpy的“娘親們”,甚至把自己誇上了天,說自己如何如何高效。而國內的一些偽專家們,也是盲目的“助紂為虐”,說什么如果你不太懂,請不要輕易去優化numpy雲雲,難道你自認為優化的算法能勝過numpy里內置的久經考驗的算法?
真的是誤人子弟!很多人在這里就被唬住了,代碼分析到numpy的環節,就不敢往下走了。
我想說的是,對一切永遠保持懷疑的精神才是真正的科學素養,是不是真的高性能,一切要用數據說話。
剛開始,我也被短暫的唬住了,畢竟numpy的底層也沒接觸過,但profiler分析的結果告訴我,問題就出在numpy里,結果發現在我的項目場景里,使用dict能完全替代numpy的所有操作,性能一下提高了很多,而numpy的高效在於ndarray
所以,采取什么數據結構要看應用場景,沒有萬能的高效數據結構
不要以為排除法是萬能的
優化代碼的過程中,因為我的以往成功“經驗”,也導致走了不少彎路,最主要的,就是盲目使用排除法。使用排除法只能使用二分查找或快排的策略去組織代碼,如果目標代碼比較少還可以,事實上,在真實場景中往往有成百上千行目標代碼。人工執行和實現O(logN)量級的操作,似乎是一種蠻干。
這里有幾個度量工具順便記錄下:
py_spy
https://github.com/benfred/py-spy
方便的生成CPU執行方法的火焰圖
line_profiler
https://github.com/rkern/line_profiler
逐行代碼分析,不要小看它的能力,它還可以指定要分析的方法和模塊
量變真的會引起質變
在很多人的習慣性邏輯思維里,一個程序的性能,隨着代碼的優化,會是一條平滑的增長曲線。但實踐表明,這個邏輯確實有問題。
通過不斷對代碼的優化,我發現,程序的性能到達一定階段會發生“突變”,或者“階躍”。上一次優化的執行時間幾百毫秒,下一次優化后的執行時間竟然只有幾十毫秒,說發生了“階躍”一點都不誇張。
為什么會這樣?
至少在我的朋友圈里,還沒有人能給我令人信服的答案,我自認為比較可靠的理解是,現代操作系統在cpu指令的處理上,對cpu的任務分配還不是那么“流暢”。
哪位朋友有好的見解,歡迎批評指正!