尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️
作者|Nick Humrich
譯者|周元昊
因為某個語言速度快而選擇其為開發你應用的語言是不成熟優化的一種體現。是的,Python 比較慢,但其犧牲性能可以提升工作效率。看完本文,相信你對 Python 會有一個全新的看法。
註:本文最初發布於 hackernoon,經原作者 Nick Humrich 授權由 InfoQ 中文站翻譯並分享。原文鏈接見:
https://hackernoon.com/yes-python-is-slow-and-i-dont-care-13763980b5a1
寫在前面
讓我們來討論一個我最近一直在思考的問題:Python 的性能。順便說一下,我是 Python 的忠實擁躉,我在各種情況下都會積極嘗試使用 Python 來解決問題。大家對 Python 最大的抱怨就是它的速度慢。有些人甚至因為 Python 的速度不如某個語言而拒絕使用它。本文中我將闡述,即便 Python 這麼慢,為什麼還值得你對它進行嘗試。
速度不再關鍵
之前,程序的運行時間相當長。CPU 資源和內存資源都十分珍貴,程序的運行時間在這種情況下是一個重要指標。計算機本身十分昂貴,當然還有隨之而來昂貴的電力消耗。優化這些資源就十分必要,因為在商業世界有一個永恒的規則:
優化你最昂貴的資源。
歷史上,程序最昂貴的資源是計算機的運行時間。這也就導致了對計算機科學的研究更專注於不同算法的效率。然而在當下環境中,這已經不再適用,現在矽的價格已經十分便宜了。是真的非常便宜。運行時間不再是你最昂貴的資源。一個公司最昂貴的資源現在是其雇傭的員工的時間。也就是正在看這篇文章的你自己的時間。對現在的公司來說,完成項目比讓項目跑得更快更重要。這點非常重要,這里再次強調:
完成項目比讓項目跑得更快更重要。
你也許會說「我們公司對性能要求很高,我構建的網站應用需要所有的請求在 X 毫秒內返回。」或者「客戶認為我們的應用慢而放棄使用我們的應用。」在這里我不是說速度根本不重要,我只是想說明速度不再是最重要的指標,因為它不再是你最昂貴的資源。
速度!
速度是唯一重要的事情
在編程的世界中當你提到速度,一般是指程序的性能,也就是 CPU 周期。而當你的 CEO 提到速度,他通常指的是業務上的速度,其中最重要的是投入市場的時間。你的產品或網路應用有多快並不重要,應用採用哪種語言編寫的也不重要,甚至是使項目運行投入了多少資金都不重要。最終,唯一能夠讓你的公司存活下來的是產品投入市場的時間。
這里不是指初創公司觀念中的盈利時間,而更多是從想法轉換到實際消費者手中所花費的時間。在商業世界中能存活下來的唯一方法是比你的競爭對手更快地進行創新。如果你的競爭對手比你更早地發布產品,那麼你有再多的好點子也無濟於事。你必須成為市場的第一個進入者,或至少要趕上領先的節奏。一旦你掉隊了,那麼你就大勢已去。
在商業世界中能存活下來的唯一方法是比你競爭對手更快地進行創新。
微服務的例子
亞馬遜、Google、Netflix 等公司深刻理解速度的重要性。它們創建了一個能快速發展和創新的業務系統。微服務就是這個問題的解決方案。本文並不討論你是不是應該使用微服務,但最起碼亞馬遜和Google認為它們應採用微服務。
微服務天生就很慢。微服務的最基礎的概念就是拆分業務邊界,並通過網路調用來相互通訊。這也就意味著你需要把一個只占幾個 cpu 周期的方法調用轉換成網路調用。從性能層面上來說,這簡直糟糕透頂。網路調用的速度和 CPU 調用根本不可同日而語。但是那些大公司仍然選擇使用微服務。沒有比微服務更慢的架構了。
微服務的最大劣勢就是其性能,但是它所帶來的最大好處是縮短了投入市場需要的時間。通過構建小型項目和少量代碼的團隊,公司可以以一個非常快的速度進行迭代與演進。這個例子只是為了展示不僅僅是初創公司,大公司也關注投入市場所需的時間。
CPU 並非你的瓶頸
如果你編寫像網路服務器上的網路應用,那麼 CPU 時間可能並非你應用的瓶頸。當你的網路服務器處理一個請求,它可能會需要調用多個網路調用,例如數據庫或 Redis 緩存。這些服務本身速度很快,然而網路調用的過程卻很慢。一篇博客很好地描述了各個特定操作速度上的差別。其中,作者將 CPU 時間對應到人們易於理解的時間。如果單個 CPU 周期對應一秒的話,一個從加利福尼亞到紐約的網路調用就大約相當於 4 年。
對,網路調用就是這麼慢。粗略地可能,在同一數據中心內的一個普通的網路調用需要 3 毫秒,這在前面的對應關係下相當於 3 個月。現在假如你的程序是 CPU 密集型的,需要花費 100,000 個 CPU 周期來處理一次調用。按之前的比例來算,這些時間相當於 1 天。那麼如果你用一個慢 5 倍的語言,它也就只花費了 5 天。相對於 3 個月的網路調用,4 天的差別就無足輕重了。如果用戶在等待一個至少需要 3 個月的包裹,那麼 4 天的差別相對來說就不那麼重要了。
說了這麼多我只是想說,即便 python 很慢,但這並不重要。語言的速度(也就是 CPU 時間)幾乎不會導致問題。Google就這個概念做過一個研究,並寫了一篇論文。論文中談論了設計高吞吐量的系統。在結論中這樣描述到:
在一個高吞吐量的環境中使用一個解釋型語言看似矛盾,但是我們發現 CPU 時間幾乎不是瓶頸因素,表達性強的語言意味著大部分代碼是短小的,大多數時間花費在了 I/O 以及原生代碼運行時上。此外,解釋型的做到所具備的靈活性十分有用,它方便了我們在語言層面上的試驗,也方便了我們探索將計算分布到多台機器上的方法。
簡單說來:
CPU 時間幾乎不是瓶頸因素。
那如果 CPU 時間的確是系統的瓶頸呢?
你可能會說「這觀點很好,但是我們確實在 CPU 上遇到了瓶頸,造成了我們網路應用的速度緩慢」,或者「在服務器上 X 語言相對 Y 語言需要更少的硬件資源來運行。」這可能都是事實。但網路應用的優勢就是你可以幾乎無限地進行負載均衡。換而言之,就是使用更多的硬件資源。當然 Python 相較其他語言,如 C 語言,可能需要更多硬件資源。那就使用更多的硬件來解決這個問題。硬件相對於你的人工時間便宜許多。如果你一年內節約了幾周的開發時間,這就遠勝於你在硬件上所節約下來的花費。
那麼,Python 到底快不快?
前面我談論了最重要的是開發所花費的時間。但是問題還是沒有得到回答:Python 的開發時間的確比其他語言快麼?經過多方調查,我、Google以及許多第三方結論都會告訴你 Python 能提升多大產能。Python 抽象化了諸多內容,可以讓你專注於你真正的業務邏輯,而不用關心你是應該使用 vector 還是 array 等底層細節問題。你可能不相信這道聽途說的觀點,所以讓我們看一些經驗數據。
總體來說,爭論 python 是否高產,最終討論的是腳本(或動態語言)與靜態類型語言之間的比較。我認為大家都讚同靜態類型語言的產量較低,但這里有一篇很好的論文解釋了其中的原因。就 Python 而言,曾有研究分析了不同語言編寫一個字符串處理程序所花費的時間,並做了很好的總結。
使用不同語言編寫字符串處理應用所花費的時間。(Prechelt 與 Garret)
在結論中 Python 比 Java 的生產效率高兩倍。還有其他諸多研究結果得到類似的結論。Rosetta Code 對不同語言進行了公平而深入地研究。在論文中它們將 Python 和其他腳本 / 解釋型語言進行了比較,並認為:
Python 是其中最精練的,甚至比函數式語言更好(平均短 1.2-1.6 倍)。
總體看來 Python 代碼的行數總是更少。代碼行數聽上去是一個糟糕的指標,但是多項研究顯示(包括之前提及的兩個),在各語言中輸入每行代碼的時間是不相上下的。因此,減少代碼行數也就相當於提高了生產效率。就連 C# 工程師 codinghorror 也寫了一篇文章闡述 Python 具有更高的產量。
我認為這已經足夠能說明 Python 相較於諸多其他語言更高產。這主要歸功於 Python 的開箱即用以及豐富的第三方包。這里有一篇文章簡述了 Python 和其他語言的差別。如果你不知道為什麼 Python 這麼「小」還這麼高產,我推薦你學習一下 Python 來親自體驗一下,下面將是你的第一行程序:
import __hello__
如果運行速度對你真的很重要?
上述觀點的論調聽上去像認為優化和速度根本不重要。但是事實是,許多時候運行時效率至關重要。一個例子是,你的網路應用有一個特定的端點需要相當長的時間來響應請求。同時你知道它需要有多快,也知道它要被優化到什麼程度。
在這個例子中,發生了下面兩件事:
-
我們關注到某個運行慢的端點。
-
我們認為它慢,因為我們了解什麼是足夠快,並且它沒能達到這個指標。
我們不必在應用中對每個服務進行細節調優。每個服務只需要能「足夠快」來滿足用戶的需求就夠了。用戶會發現某個端點花費了幾秒時間返回,但是他們並不會注意到你把一個 35 毫秒的請求優化到了 25 毫秒。你只需要達到「足夠好」就可以了。免責聲明:不得不說一些應用,如實時拍賣應用,確實需要細節調優,能提升一毫秒算一毫秒。但是這是一個特例,而不是業界的規則。
為了弄清如何優化某個端點,第一步你需要對你的代碼進行性能分析,並嘗試整理出其中的瓶頸。歸根到底:
任何不考慮瓶頸的調優都是幻想。—— Gene Kim
如果你的優化並不解決瓶頸,那你就是在浪費你的時間,而且還不能解決真正地問題。不解決瓶頸,你就不會在性能上得到顯著的提升。如果你嘗試著在了解瓶頸前優化,你就像在和你的代碼在玩打地鼠的遊戲。在排查和確定瓶頸前優化代碼也是「不成熟優化」的表現。Donald Knuth 常被引用下面的觀點,雖然他本人稱這也是從其他人那兒聽來的:
不成熟的優化是萬惡之源。
Donald Knuth 在一次關於維護代碼庫的討論中進行了下面的完整描述:
我們應該忘記那些小的性能提升,這占了 97% 的時間:不成熟的優化是萬惡之源。但同時我們也不能放過那至關重要的 3%。
換句話來說,大部分時間,你不應該關心代碼的優化。它們通常已經足夠好了。如果沒有能達到標準,我們應該只需要改變那 3% 的代碼。你並不會因為你的代碼使用一個 if 替代了一個方法,得到幾毫秒的性能提升而獲得任何獎勵。只有在分析之後再進行優化。
不成熟的優化包含盲目調用某個更快的方法,或使用一個特定的數據結構只因為其總體上更快。計算機科學認為兩個方法或算法有一樣的漸進增長(或時間複雜度),那麼就可以認為它們性能是相同的,就算其中之一比另一個慢兩倍。計算機的速度太快,算法在計算上的增長,如數據或使用量的增長比算法本身重要得多。
換而言之,如果你有兩個 O(log n) 的方法,一個是另一個速度的兩倍,這之間的差別根本無關緊要。隨著數據量的增長,它們都會以相同的速度變慢。這也就是為什麼不成熟的優化是萬惡之源,它會浪費我們的時間,最終卻在提升性能上幫不上我們什麼忙。
就時間複雜度而言,你可以認為用任何的語言寫你的程序的複雜度都是 O(n) 的,其中 n 是代碼的行數或指令個數。同一指令的增長速率都是相同的。所以一個語言或運行時的快慢並不重要,就漸進增長而言,所有語言都是等價的。在這個邏輯下,你可以認為,因為某個語言速度快而選擇其為開發你應用的語言是不成熟優化的一種體現。你不應該主觀地判斷某個語言快而不去進行衡量、不去了解將會遇到的瓶頸。
因為某個語言速度快而選擇其為開發你應用的語言是不成熟優化的一種體現。
優化 Python
我最喜歡 Python 的一點就是它可以讓你一步一步地優化你的代碼。比如說你有一個 Python 方法,你發現它是你項目中的瓶頸。你已經對其優化了數次,可能是遵循了這里或這里的意見,現在你確定 Python 本身是你應用的瓶頸所在。
Python 是能夠直接調用 C 代碼的,這就意味著你可以用 C 重寫這個方法來減少性能問題。你可以一個一個地進行替換。這個過程能讓你調用任何最終編譯成 C 兼容指令的優化的代碼,也讓你能在大部分情況下繼續使用 Python,而只在真正需要的時候深入底層進行開發。
有一個叫 Cython 的語言,它是 Python 的超集。幾乎是 Python 和 C 的結合體,同時它是漸進的類型化語言。任何 Python 代碼都是合法的 Cython 代碼,Cython 會將代碼編譯成 C 代碼。有了 Cython,你可以編寫模塊或方法,漸漸地引入 C 語言的類型和性能。你可以混合使用 C 語言的類型和 Python 的鴨子類型(duck type)。通過 Cython 你可以只在瓶頸處進行調優,而在其他地方仍然使用優美的 Python 語言,兩者能完美地結合。
使用 Python 編寫的太空大規模多人在線遊戲 EVE Online 的截圖
當你最終遇到了 Python 的性能瓶頸,你不需要將你所有代碼移植到其他語言。你總是可以使用 Cython 重寫部分方法來滿足性能上的需求。這也是遊戲 EVE Online 所採用的策略。Eve 是一個大型多人在線電腦遊戲,它完全使用 Python 和 Cython 開發。遊戲開發人員通過在 C/Cython 中調優瓶頸來達到遊戲級的性能要求。如果遊戲都能達到性能上的需求,那麼大部分情況都應該可以滿足。
此外,還有其他方法來優化你的 Python 程序。例如 PyPy 是一個 Python 的運行時編譯執行(JIT)的做到,只需要使用 PyPy 切換默認的 Cython,就可以顯著地提升你長時間運行應用的運行時性能,如在網路服務器上。
讓我們回顧一下文中的要點:
-
優化你最昂貴的資源。也就是你自己,而不是電腦。
-
選擇可以有助於快速開發的語言、框架、架構,例如 Python。不要只因為運行速度快而選擇某個技術。
-
當你的應用有性能問題時,找出你應用中性能的瓶頸。
-
你的瓶頸通常不是 CPU 或 Python 本身。
-
如果你已經優化了算法或其他方面,確定 Python 的確是你項目的瓶頸,那麼可以將這個熱點移到 Cython/C 中進行改寫。
-
接下來就坐下來享受快速編碼的樂趣吧。
安利一個活動
AWS 在線研討會:人工智能專題免費報名——Apache MXNet 是一種功能全面、可以靈活編程並且擴展能力超強的深度學習框架,支持包括卷積神經網路 (CNN) 與長短期記憶網路 (LSTM) 在內的頂尖深度模型。本次研討會介紹如何在 AWS 上運行 MXNet,借助於深度學習 AMI 和 CloudFormation 模板,使深度學習的開發人員可以獲得高度可擴展、靈活且快速的模型訓練體驗。點擊閱讀原文報名!
今日薦文
點擊下方圖片即可閱讀
左耳朵耗子:技術一定會讓人失業,但我沒有生不逢時