關于Python的幾點思考
簡介:一直以來,Python的性能是大家詬病最多的地方,不少最初采用Python的項目甚至開始遷移到其他語言,Duolingo就是其中一例。而整個Python社區最成功的框架莫過于PyPy,但Python使用大戶Dropbox并沒有采用,相反,他們另起爐灶寫了一個Pyston。關于Python幾個老生常談的話題,Kevin Modzelewski有話要說。
一直以來,Python的性能是大家詬病最多的地方,不少最初采用Python的項目甚至開始遷移到其他語言,Duolingo就是其中一例。而整個Python社區最成功的框架莫過于PyPy,但Python使用大戶Dropbox并沒有采用,相反,他們另起爐灶寫了一個Pyston。關于Python幾個老生常談的話題,Kevin Modzelewski有話要說。
“出來混遲早要還的”技術債
Duolingo (多鄰國)是一個完全免費的多國語言學習工具,致力于為全球用戶提供一個平等接受語言教育的機會。目前用戶數超過1.5億人(筆者是用戶之一),其創始人 Luis von Ahn 是卡內基梅倫大學的教授、圖靈測試( CAPTCHAs ,俗稱驗證碼)的發明人、麥克阿瑟家族的一員。
不久前Duolingo工程師Andrekhorie在官方技術博客上 發表了一篇文章 ,闡述了對 Duolingo 這個復雜系統重構的種種心得。在 Duolingo 后端,超過88個課程系統通過機器學習的不斷優化后提供給用戶使用。 Duolingo 原來的系統是用 Python 編寫的,后來用 Scala 重寫了一遍。雖然重構系統耗費了大量的人力、物力(工程師們不得不中斷新的研發計劃轉而用幾個月的時間來重寫整個系統),但這一切都是值得的——重構后的系統時延從750ms降到14ms,uptime則從99.9%提高到了100%。
系統重構
沒有什么例外,所有的公司都會在成長初期欠下這樣的 技術債 ——就像你欠銀行的錢,隨著時間的推移它會像滾雪球一樣不斷變大。有句話說的好,出來混遲早是要還的。這種痛苦將伴隨著企業的成長,直至你不得不面對它。
在系統設計之初,由于經常需要共享數據, Duolingo 的系統架構如下圖所示:
這個架構的問題是有太多的嚴重依賴關系,在這種情況下,越來越多的網絡請求使得系統延時越來越大。為了解決這個問題,重新設計架構的主導思想是盡可能解耦所有的依賴,使系統變得簡單和健壯(如下圖所示)。在新的架構下,經常被共享使用的課程數據離線存儲在 AWS S3 上, Duolingo 只需做一些輕量級的緩存即可。
重寫代碼
Duolingo 做的最大的一個決定是用 Scala 重寫了會話生成器。 Duolingo 后端的會話生成器最初是用 Python 編寫的。 Python 的優點就不多說了,但它的缺點也是顯而易見的。
- 性能 :例如Python比C或者Java要慢很多。
- 內存管理 :Python的 GIL 制約了開發人員對內存的管理和利用。
- 動態類型 :對于復雜系統,這導致部署過程中不得不重復大量的bug修復工作,反而減慢了項目的實施進度。
作為一門函數式編程語言, Scala 有很多十分吸引人的特性。它十分精煉,編寫的代碼可讀性更強、調試維護也更方便。 Scala 吸取了其前輩編程語言的優點,并解決了那些語言無法克服的痛點,有更好的容錯機制,bug也更少。此外, Scala 基于 Java Virtual Machine 意味著有豐富的Java類庫可用。在很多復雜度不比 Duolingo 差的大數據項目中都使用了 Scala ,這看起來是個不錯的選擇。最重要的是在復雜系統編程語言中, Scala 的學習曲線比較容易。
Duolingo的思考
通過這次重構, Duolingo 寫了所有的測試用例,一方面提高了系統的穩定性,另一方面積累了許多開發文檔。在這個過程中他們無縫集成了許多互相獨立的開發組件。
本次重構花費的時間低于 Duolingo 的預期,并且重構后的系統更健壯,代碼庫也更可讀、可維護。在整個重構過程中也遇到了一些痛點,比如 Scala 的一些庫缺乏可用的文檔;在與Java集成時也遇到了一些麻煩,某些 Scala 的特性不能被很好地支持。
性能數據方面,重構后的系統在最初的幾個月里高可用達到100%,而這一數據最初是99%,大部分的延時都發生在從S3的緩存中下載沒有被緩存命中的課程數據文件。
經常吐槽Python的同學“你不懂”
鑒于最近大家對 關于Pyston的未來 討論較多,恰好近日 Pyston發布了0.6.1版 ,最新版的 Pyston 在基準測試中的性能比 CPython 快95%,并使 Dropbox 的性能提升了10%(Dropbox內部有很多項目是用Python編寫的, Pyston 是Dropbox發起的一個開源項目,目標是使用LLVM和現代JIT技術開發一款高性能的Python實現)。 Pyston 項目的核心開發者Kevin Modzelewski寫了 一篇文章 ,談了他對 Python 的一些看法。
為什么要有JIT以及為什么要重復造輪子
時光倒流至2013年,當時 Dropbox 網站的Web服務器90%的CPU資源被各種訪問請求消耗掉了,服務器采購的速度讓所有人都心驚肉跳。當時普遍認為 Python 的瓶頸是I/O,由于這個問題在整個系統中廣泛存在,問題相當棘手。放眼望去當時并無成熟的技術解決方案——你不能指望也無法想象幾百萬行代碼的 PyPy 能夠與 Dropbox 自身的大量擴展兼容。時至今日Kevin Modzelewski依然堅信,當初的選擇是正確的。人們既不想放棄 Python 這個生態,又想提高性能,唯有想方設法改進 Python ,這是不二之選。
另一個常見的抱怨是,當初為什么不采用 PyPy 或 CPython 的代碼庫?這個問題確實不好回答。兼容性是 Dropbox 首先要考慮的,其次是合理的性能提升。 Dropbox 的需求與 PyPy 在設計理念和技術實現兩方面都背道而馳,在Kevin Modzelewski看來,這也是 PyPy 項目繼續取得更大成就的瓶頸所在。對 PyPy 的小修小補固然能有所改進,但像內存使用、支持C擴展、性能穩定等問題已經固化到 PyPy 的架構中去,這顯然不是修修補補能解決的問題。Kevin Modzelewski對一個為了適用于 Dropbox 而改造過的 PyPy 到底還能不能再稱之為 PyPy 表示懷疑。
至于 CPython ,這更多的是一個務實的決定, Dropbox 當時的目標是盡可能多地利用 CPython 。Kevin Modzelewski也承認,時至今日,90%的 Pyston 的代碼庫都是 CPython 代碼。從這一點上來說, Pyston 顯然是基于 CPython 的實現。但是,在項目的早期階段, Dropbox 的首要任務是驗證其戰略是否正確——利用LLVM開發一款高性能的 Python 實現, CPython 并不適合進行這樣的實驗。盡管走了一些彎路、重復造了一些輪子,Kevin Modzelewski認為 Dropbox 折騰一番的價值在于他們更好地理解了這些技術,未來也許還會遇到類似的情況,由于前面的經驗相信他們能做的更好。
一些大家沒想到的點
在Kevin Modzelewski看來,沒有調查就沒有發言權,但是一些人比較熱衷于發表不切實際的看法。比如說,V8引擎使得瀏覽器運行JS的速度變得更快,Kevin Modzelewski在想的是如何使 Python 也能獲得這樣的速度。Kevin Modzelewski考慮的是,跟動態語言相比,為什么 Python 比較慢呢?而不是說,你看Lua的速度更快,所以用Lua會更好。Kevin Modzelewski希望人們理解, Python 之所以慢是因為其豐富的對象模型,而不是它的動態類型和動態范圍。問題是,每一個 Python 操作都有很多關鍵點,由于很多特性被廣泛使用,因此用戶就忽略掉了這些地方。
比如,當一個包含局部變量的框架退出后,如何平滑地立即修改對應的函數?在缺少動態語言如JS或Lua那樣的模擬機制的前提下,這些特性由于應用廣泛所以必須支持。但是人們顯然忽略掉了這些。
另一方面, Python 的兼容性也跟大家理解的不一樣。例如,Kevin Modzelewski發現 Dropbox 的代碼庫太龐大以至于兼容性問題不可避免。當從引用計數切換到跟蹤垃圾收集器甚至切換到排序字典時,都會引起無數的中斷。最終 Dropbox 不得不分離并重新執行這兩個實現以匹配 CPython 的行為。
特別是在Web應用程序領域內存使用是大家詬病 Python 最多的地方。一部分是GIL的機制問題——類似于多線程,多進程肯定會占用更多的內存。不管出于怎樣的考慮, Python 禁止不同的進程之間共享其內存。這里的內存使用不是大家經常討論的在Python空間(MicroPython除外)中的內存使用,而且這也是 PyPy 不適用于 Dropbox 的另一個原因。 Dropbox 系統中的許多地方實際上也是有這種限制,其中一個關鍵指標是“每GB內存的每秒請求次數”。Kevin Modzelewski一度認為,當請求次數以50%的速度遞增,而內存以2倍的速度擴展是可行的,實際上這在內存綁定類服務中根本不可行。
Kevin Modzelewski最后表示,盡管有上述問題,但這在當時都是經過考量的選擇,如果再來一次他相信能做的更好。親愛的讀者,你對Python的認識是否因此得以刷新了呢?
來自:http://www.infoq.com/cn/articles/some-thoughts-on-python