## python規範 - 函數必須寫註釋:文檔註釋格式`'''註釋內容'''` - 參數中的等號兩邊不要用空格 - 相鄰函數用兩個空行隔開 - 小寫 \+ 下劃線 1. 函數名 2. 模塊名 3. 實例名 - 駝峰法 1. 類名 ## tips ```python # 一行代碼太長,使用\折行 i ...
1提高數據處理效率的迫切需要
本章包括
- 處理指數級增長的數據所面臨的挑戰
- 傳統計算架構與最新計算架構的比較
- Python在現代數據分析中的作用和不足
- 提供高效Python計算解決方案的技術
我們一直在以極快的速度從各種來源收集海量數據。無論目前是否有使用價值,這些數據都會被收集起來。無論是否有辦法對其進行處理、存儲、訪問或學習,數據都會被收集起來。在數據科學家對其進行分析之前,在設計師、開發人員和政策制定者利用其創造產品、服務和程式之前,軟體工程師必須找到存儲和處理這些數據的方法。現在,這些工程師比以往任何時候都更需要有效的方法來提高性能和優化存儲。
在本書中,我將分享我在工作中使用的一系列性能和存儲優化策略。簡單地增加機器數量往往既不可能,也無濟於事。因此,我在這裡介紹的解決方案更多地依賴於理解和利用我們手頭的東西:編碼方法、硬體和系統架構、可用軟體,當然還有Python語言、庫和生態系統的細微差別。
Python已經成為一種首選語言,可以完成或至少粘合所有與數據洪流有關的繁重工作,這也是老生常談的說法。事實上,Python在數據科學和數據工程領域的流行是該語言發展的主要推動力之一,根據大多數開發人員調查,Python已成為最流行的三大語言之一。在處理大數據方面,Python有其獨特的優勢和局限性,速度不夠快肯定會帶來挑戰。好的一面是,正如您將看到的,有許多不同的角度、方法和變通辦法可以讓Python更高效地處理大量數據。
在找到解決方案之前,我們需要充分理解問題,而這正是我們在第一章中要做的事情。我們將花一些時間仔細研究大量數據帶來的計算挑戰,以確定我們所面對的問題到底是什麼。接下來,我們將研究硬體、網路和雲架構的作用,以瞭解為什麼提高CPU速度等舊的解決方案不再適用。然後,我們將討論Python在處理大數據時面臨的特殊挑戰,包括Python的線程和CPython的全局解釋器鎖(GIL)。一旦我們充分理解了需要新的方法來使Python性能更佳,我將概述您將在本書中學到的解決方案。
1.1 數據洪水有多嚴重?
您可能知道摩爾定律和埃德霍姆定律這兩個計算定律,它們共同描繪了數據呈指數級增長,而計算系統處理這些數據的能力卻滯後的戲劇性畫面。埃德霍姆定律指出,電信領域的數據傳輸速率每18個月翻一番,而摩爾定律則預測,微晶元上可容納的晶體管數量每兩年翻一番。我們可以把埃德霍爾姆數據傳輸速率作為收集數據量的代表,把摩爾晶體管密度作為計算硬體速度和容量的指標。當我們把它們放在一起時,就會發現我們收集數據的速度和數量與我們處理和存儲數據的能力之間存在六個月的差距。由於指數式增長很難用語言來理解,因此我將這兩個定律對照繪製成一張圖,如圖1.1所示
摩爾定律和埃德霍爾姆定律之間的比率表明,硬體將始終落後於所產生的數據量。而且,隨著時間的推移,這種差距會越來越大。
這張圖描述的情況可以看作是我們需要分析的內容(埃德霍姆定律)與我們進行分析的能力(摩爾定律)之間的鬥爭。實際上,這幅圖描繪的情況比我們的實際情況更為樂觀。我們將在第 6 章討論現代CPU架構中的摩爾定律時瞭解其中的原因。這裡我們重點討論數據增長,舉一個例子,互聯網流量是可用數據的間接衡量標準。如圖1.2所示,多年來互聯網流量的增長很好地遵循了埃德霍姆定律。
圖 1.2 多年來全球互聯網流量的增長(以每月 PB 為單位)。(資料來源:https://en.wikipedia.org/wiki/Internet_traffic)
此外,人類產生的 90% 的數據都發生在過去兩年(參見"大數據及其意義",http://mng.bz/v1ya )。至於這些新數據的質量是否與其規模成正比,則完全是另一回事。問題是,產生的數據需要處理,而處理需要資源。
給軟體工程師帶來障礙的不僅僅是可用數據的數量。所有這些新數據的表現方式在本質上也在發生變化。有人預測,到2025年,大約80%的數據可能是非結構化數據("挖掘非結構化數據的力量",http://mng.bz/BlP0 )。我們將在本書稍後部分詳細介紹,但簡單地說,從計算角度來看,非結構化數據對數據處理的要求更高。
我們如何處理這些增長的數據?事實證明,我們大多沒有這樣做。據《衛報》(http://mng.bz/Q8M4 )報道,超過99%的數據從未被分析過。我們之所以無法利用如此多的數據,部分原因在於我們缺乏分析這些數據的有效程式。
數據的增長以及隨之而來的對更多處理的需求,已發展成為有關計算的最惡毒的咒語之一: "如果你有更多的數據,只需投入更多的伺服器"。出於多種原因,這往往不是一個可行或合適的解決方案。相反,當我們需要提高現有系統的性能時,我們可以審視系統架構和實施,找到可以優化性能的地方。我已經記不清有多少次在審查現有代碼時,只需註意效率問題,就能將性能提高十倍。
需要明白的是,需要分析的數據量的增加與分析數據所需的基礎設施的複雜性之間幾乎不是線性關係。解決這些問題需要開發人員花費比機器更多的時間和智慧。這不僅適用於雲環境,也適用於內部集群,甚至適用於單機實施。一些使用案例將有助於說明這一點。例如
您的解決方案只需要一臺電腦,但突然您需要更多的機器。增加機器意味著你必須管理機器的數量,在它們之間分配工作負載,並確保數據被正確分區。您可能還需要一個文件系統伺服器來增加機器數量。維護伺服器群或雲的成本要比維護單台電腦的成本高得多。
您的解決方案在記憶體中運行良好,但隨著數據量的增加,已不再適合您的記憶體。要處理存儲在磁碟中的新數據量,通常需要重新編寫代碼。當然,代碼本身的複雜性也會增加。例如,如果主資料庫現在在磁碟上,您可能需要創建緩存策略。或者,您可能需要從多個進程進行併發讀取,或者更糟糕的是,併發寫入。
在使用SQL資料庫時,突然達到了伺服器的最大吞吐能力。如果只是讀取能力問題,那麼只需創建幾個讀取副本就能解決。但如果是寫入問題,該怎麼辦呢?也許你會建立分片,或者決定徹底改變你的資料庫技術,轉而使用一些所謂性能更好的 NoSQL 變體?
如果您依賴的是基於供應商專有技術的雲系統,您可能會發現,無限擴展的能力更多是營銷上的空談,而不是技術上的現實。在許多情況下,如果您遇到性能限制,唯一現實的解決方案就是改變您正在使用的技術,而這種改變需要大量的時間、金錢和人力。
我希望這些例子能夠說明,增長並不僅僅是"增加更多機器"的問題,而是需要在多個方面開展大量工作,以應對日益增加的複雜性。即使是在單台電腦上實施並行解決方案這樣"簡單"的事情,也會帶來並行處理的所有問題(競賽、死鎖等)。這些更高效的解決方案會對複雜性、可靠性和成本產生巨大影響。
最後,我們可以提出這樣的觀點:即使我們可以線性擴展我們的基礎設施(我們確實做不到),也需要考慮倫理和生態問題:據預測,與"數據海嘯"相關的能源消耗占全球發電量的20%("數據海嘯",http://mng.bz/X5GE ),而且隨著硬體的更新,還存在垃圾填埋問題。
好消息是,在處理大數據時提高計算效率可以幫助我們減少計算費用、解決方案架構的複雜性、存儲需求、上市時間和能源消耗。有時,更高效的解決方案甚至可以將實施成本降到最低。例如,明智地使用數據結構可以減少計算時間,而無需大量的開發成本。
另一方面,我們將研究的許多解決方案都需要開發成本,而且本身也會增加一定的複雜性。當您查看您的數據並預測其增長時,您必須判斷在哪些方面進行優化,因為沒有一目瞭然的方法或一刀切的解決方案。儘管如此,可能有一條規則可以適用於所有情況:如果解決方案對Netflix、谷歌、亞馬遜、蘋果或 Facebook 有利,那麼它可能對你不利,當然,除非你在這些公司中工作。
我們大多數人所看到的數據量將大大低於最大的技術公司所使用的數據量。數據量仍然會很大,數據仍然會很難獲取,但可能會低幾個數量級。在我看來,認為適用於這些公司的技術也適用於我們其他人的觀點是錯誤的。一般來說,不太複雜的解決方案更適合我們大多數人。
正如你所看到的,在這個數據和演算法的數量和複雜性都急劇增長的新世界里,需要更複雜的技術,以高效和節約成本的方式進行計算和存儲。不要誤解我的意思:有時你需要擴展你的基礎設施。但是,在設計和實施解決方案時,您仍然可以使用同樣的思維方式,註重效率。只是技術不同而已。
1.2 現代計算架構和高性能計算
創建更高效的解決方案並非抽象空洞。首先,我們要考慮我們的領域問題,即您要解決的實際問題是什麼。同樣重要的是運行解決方案的計算架構。計算架構在決定最佳優化技術方面發揮著重要作用,因此我們在設計軟體解決方案時必須將其考慮在內。本節將介紹影響解決方案設計和實施的主要架構問題。
1.2.1 電腦內部的變化
電腦內部正在發生翻天覆地的變化。首先CPU處理能力的提高主要體現在並行單元的數量上,而不是像過去那樣體現在原始速度上。電腦還可以配備圖形處理器(GPU),這種處理器最初只用於圖形處理,但現在也可用於通用計算。事實上,許多人工智慧演算法的高效實現都是通過GPU實現的。不幸的是,至少從我們的角度來看GPU的架構與CPU完全不同:它們由數千個計算單元組成,所有單元都要進行相同的"簡單"計算。記憶體模型也完全不同。這些差異意味著,GPU的編程需要一種與 CPU 完全不同的方法。
要瞭解如何將GPU用於數據處理,我們需要先瞭解GPU的原始用途和架構含義。顧名思義,GPU是為圖形處理而開發的。一些對計算要求最高的應用實際上就是游戲。游戲和一般的圖形應用都需要不斷更新屏幕上的數百萬像素。為解決這一問題而設計的硬體架構有許多小型處理核心。GPU很容易擁有數千個內核,而CPU通常只有不到10個。GPU內核要簡單得多,而且每個內核大多運行相同的代碼。因此,它們非常適合運行大量類似的任務,如更新像素。
鑒於GPU強大的處理能力,人們試圖將其用於其他任務,於是出現了圖形處理器通用計算(GPGPU)。由於GPU架構的組織方式,它們大多適用於大規模並行性質的任務。事實證明,許多現代人工智慧演算法,如基於神經網路的演算法,往往是大規模並行的。因此,兩者之間存在著天然的契合點。
不幸的是,CPU和GPU 之間的區別不僅在於內核數量和複雜性。GPU記憶體,尤其是計算能力最強的 GPU,是與主記憶體分離的。因此,主記憶體和GPU記憶體之間還存在數據傳輸問題。因此,我們在使用 GPU 時需要考慮兩個巨大的問題。
在第9章中我們會清楚地看到,使用Python對GPU進行編程要比針對CPU的編程困難得多,也更不實用。儘管如此,Python仍然有足夠的空間來使用GPU。
與GPU的進步相比,CPU的編程方式雖然沒有那麼時髦,但也發生了巨大的變化。與GPU不同的是,我們可以在Python中輕鬆使用CPU的大部分變化。與過去相比,CPU性能的提升是由製造商以不同的方式實現的。在物理定律的驅動下,他們的解決方案是建立更多的並行處理,而不是更快的速度。摩爾定律有時被表述為速度每24個月翻一番,但實際上這並不是正確的定義:它是指晶體管密度每兩年翻一番。速度提升與晶體管密度之間的線性關係早在十多年前就已打破,此後速度基本趨於平穩。鑒於數據隨著演算法複雜度的增加而持續增長,我們正處於一種有害的境地。中央處理器製造商提出的第一類解決方案是允許更多並行性:每台電腦擁有更多的中央處理器、每個中央處理器擁有更多的內核,以及同時進行多線程處理。處理器不再真正加速順序計算,而是允許更多併發執行。這種併發執行要求我們改變電腦編程的模式。以前,當你更換CPU時,程式的速度會"神奇"地提高。現在,速度的提高取決於程式員是否意識到底層架構向並行編程範式的轉變。
現代CPU的編程方式發生了許多變化,正如你在第6章中看到的,其中一些變化非常反直覺,值得從一開始就加以關註。例如,雖然CPU的速度近年來有所下降,但CPU的速度仍然比RAM快幾個數量級。如果CPU緩存不存在,那麼CPU大部分時間都會閑置,因為它們大部分時間都在等待RAM。這意味著,有時處理壓縮數據(包括解壓縮的成本)比處理原始數據更快。為什麼呢?如果可以將壓縮塊放在 CPU 緩存中,那麼那些等待 RAM 訪問的空閑周期就可以用來解壓縮數據,而空餘的 CPU周期則可以用於計算!類似的論點也適用於壓縮文件系統:它們有時比原始文件系統更快。這在Python世界中也有直接的應用;例如,通過改變一個簡單的布爾標誌來選擇NumPy數組的內部表示法,就可以利用緩存的局部性問題,大大加快NumPy處理速度。表1.1列出了不同類型記憶體的訪問時間和大小,包括CPU緩存、RAM、本地磁碟和遠程存儲。這裡的關鍵不是精確數字,而是大小和訪問時間的數量級差異。
表 1.1 包括電腦外部的三級存儲。這裡也發生了一些變化,我們將在下一節討論。
參考資料
- 軟體測試精品書籍文檔下載持續更新 https://github.com/china-testing/python-testing-examples 請點贊,謝謝!
- 本文涉及的python測試開發庫 謝謝點贊! https://github.com/china-testing/python_cn_resouce
- python精品書籍下載 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
- Linux精品書籍下載 https://www.cnblogs.com/testing-/p/17438558.html
1.2.2 網路中的變化
在高性能計算環境中,我們使用網路來增加存儲,尤其是提高計算能力。雖然我們希望使用單台電腦解決我們的問題,但有時依靠計算集群是不可避免的。優化多台電腦的架構--無論是在雲端還是在企業內部--將是我們通往高性能之路的一部分。
使用多台電腦和外部存儲會帶來與分散式計算相關的全新問題:網路拓撲結構、跨機器共用數據以及管理跨網路運行的進程。這樣的例子不勝枚舉。例如,在需要高性能和低延遲的服務上使用REST API的代價是什麼?我們如何處理遠程文件系統帶來的懲罰;我們能否減輕這些懲罰?
我們將努力優化網路堆棧的使用,為此,我們必須瞭解圖1.3所示的各個層面。在網路之外,我們有自己的代碼和Python庫,它們會對下麵的層進行選擇。在網路堆棧的頂層,數據傳輸的典型選擇是基於 JSON 有效載荷的 HTTPS。
雖然這對許多應用程式來說是一個完全合理的選擇,但在網路速度和滯後很重要的情況下,還有性能更高的替代方案。例如,二進位有效載荷可能比JSON更有效。此外,HTTP可能會被直接的TCP套接字取代。但也有更激進的替代方案,如替換TCP傳輸層:大多數互聯網應用協議都使用TCP,但也有少數例外,如DNS和DHCP,它們都基於UDP。TCP協議具有很高的可靠性,但這種可靠性需要付出一定的性能代價。有時,UDP較小的開銷會是更有效的選擇,而額外的可靠性則沒有必要。
在傳輸協議之下,我們還有互聯網協議(IP)和物理基礎設施。在我們設計解決方案時,物理基礎設施可能非常重要。例如,如果我們有一個非常可靠的本地網路,那麼可能丟失數據的UDP將比在不可靠的網路中更有優勢。
1.2.3 雲
過去,大多數數據處理實施都是在單台電腦或由運行工作負載的同一機構維護的內部集群上進行的。目前,所有伺服器都是"虛擬"的、由外部實體維護的雲基礎設施正變得越來越普遍。有時,就像所謂的無伺服器計算一樣,我們甚至不直接與伺服器打交道。
雲不僅僅是增加更多的電腦或網路存儲。它還涉及如何處理存儲和計算資源的一系列專有擴展,而這些擴展會對性能產生影響。此外,虛擬電腦還可能影響某些CPU優化。例如,在裸機中,你可以設計一個考慮到緩存位置問題的解決方案,但在虛擬機中,你無法知道你的緩存是否被同時執行的另一個虛擬機搶占。在這樣的環境中,我們該如何保持演算法的效率呢?此外,雲計算的成本模式完全不同,時間就是金錢,因此高效的解決方案變得更加重要。
雲計算中的許多計算和存儲解決方案都是專有的,具有非常特殊的應用程式介面和行為。使用這些專有解決方案也會對性能產生影響,這一點應加以考慮。因此,雖然與傳統集群有關的大多數問題也適用於雲,但有時會有一些特殊問題需要單獨處理。既然我們已經瞭解了架構的可能性和局限性,這些可能性和局限性將影響我們的應用程式,下麵我們就來談談Python在高性能計算方面的優缺點。
1.3 Python的局限性
Python廣泛應用於現代數據處理應用程式。與任何語言一樣,它既有優點,也有缺點。使用Python有很多理由,但在這裡我們更關註的是如何處理 Python 在高性能數據處理中的局限性。
讓我們不要粉飾現實:在處理高性能計算方面,Python的能力遠遠不夠。如果只考慮性能和並行性,就不會有人使用Python。Python擁有令人驚嘆的數據分析庫生態、優秀的文檔和支持性極強的社區。這就是我們使用它的原因,而不是計算性能。
有一句話是這樣說的:"沒有慢的語言,只有慢的語言實現"。我希望允許我提出不同意見。要求Python(或者JavaScript)這樣的動態高級語言的實現者在速度上與 C、C++、Rust或Go這樣的低級語言競爭是不公平的。
動態類型和垃圾回收等功能將在性能方面付出代價。這沒有問題:在很多情況下,程式員的時間比計算時間更寶貴。但我們也不要把頭埋在沙子里:更多的聲明性和動態語言將在計算和記憶體方面付出代價。這是一種平衡。
儘管如此,這並不能成為性能不佳的語言實現的藉口。在這方面,您可能正在使用的Python旗艦實現CPython的表現如何?要進行全面分析並非易事,但您可以做一個簡單的練習:編寫一個矩陣乘法函數並計時。例如,用另一種Python實現(如PyPy)運行它。然後將代碼轉換為 JavaScript(這是一種公平的比較,因為該語言也是動態語言;不公平的比較則是 C 語言)並再次計時。
1.3.1 全局解釋器鎖(Global Interpreter Lock)
CPython有GIL,它只允許一個線程在一個時間點上執行。即使在多核處理器上,在一個時間點上也只能執行一個線程。
Python的其他實現,如Jython和IronPython,沒有GIL,可以使用現代多核處理器的所有內核。但CPython仍是所有主要庫開發的參考實現。此外,Jython和IronPython分別依賴於JVM和.NET。因此,CPython憑藉其龐大的庫基礎,最終成為預設的Python實現。我們將在書中簡要討論其他實現,其中最著名的是PyPy。
發是指一定數量的任務可以在時間上重疊,儘管它們可能不是同時運行。例如,它們可以交錯運行。並行是指任務同時執行。因此,在 Python 中,併發是可能的,但並行是不可能的......。
沒有並行的併發仍然非常有用。這方面最好的例子來自JavaScript世界和Node.JS,Node.JS被廣泛用於實現網路伺服器的後臺。在許多伺服器端網路任務中,大部分時間實際上都在等待IO;這正是一個線程主動放棄控制權的大好時機,以便其他線程可以繼續計算。現代Python 也有類似的非同步設施,我們將討論它們。
但回到主要問題:GIL 會帶來嚴重的性能損失嗎?在大多數情況下,答案是令人驚訝的否定。這主要有兩個原因:
- 大多數高性能代碼,即那些緊湊的內迴圈,很可能需要用我們討論過的低級語言來編寫。
- Python為低級語言提供了釋放 GIL的機制。
此外,多進程(即同時運行多個進程)並不受GIL的影響。
1.4 解決方案總結
本書的主題是如何從Python中獲得高性能,只有從數據和演算法需求以及計算架構等更廣闊的角度來考慮,才能設計出高效的代碼。幫助你理解CPU設計、GPU、存儲替代方案、網路協議和雲架構以及其他系統考慮因素(圖1.4)的影響,從而為提高Python代碼的性能做出正確的決策。無論是單台電腦、支持GPU的電腦、集群還是雲環境,本書都將幫助您評估計算架構的優缺點,並實施必要的更改以充分利用其優勢。
本書的目標是向您介紹一系列解決方案,並向您展示每種解決方案的最佳應用方式和應用場合,這樣您就可以針對特定的資源、目標和問題選擇並實施最高效的解決方案。我們會花大量時間舉例說明,讓你親眼目睹這些方法的正反兩方面效果。我們並沒有規定必須採用所有方法,也沒有規定採用這些方法的順序。每種方法都會或多或少地提高性能和效率,同時也會有所取捨。如果你瞭解自己的系統以及改善系統各方面的可用策略,你就可以選擇在哪些方面花費時間和資源。為了幫助您理解這些方法,表1.2概述了書中介紹的技術及其所針對的系統開發流程的組件或領域。
表中的內容很多,所以讓我強調一下主要重點領域的實際應用。讀完本書後,您將能夠查看本地Python代碼,並理解內置數據結構和演算法對性能的影響。您將能夠發現並用更合適的解決方案替換低效的結構:例如,在對恆定列表重覆搜索時用集合替換列表,或者使用非對象數組代替對象列表以提高速度。您還可以使用性能不佳的現有演算法,並(1)對代碼進行剖析,找出導致性能問題的部分,(2)確定優化這些代碼的最佳方法。
如前所述,本書針對廣泛使用的Python數據處理和分析庫(如pandas和NumPy),旨在改進我們使用這些庫的方式。在計算方面,這是一個很大的材料,因此我們不會討論非常高級的庫。例如,我們不會討論如何優化TensorFlow的使用,但會討論如何提高底層演算法的效率。
關於數據存儲和轉換,您將能夠查看數據源並瞭解其在高效處理和存儲方面的缺點。然後,您將能夠對數據進行轉換,使所有必要的信息仍然得到保留,但數據訪問模式將大大提高效率。最後,您還將瞭解到Dask,這是一個基於Python的框架,允許您開發並行解決方案,可以從單機擴展到超大型電腦集群或雲計算解決方案。
釘釘或微信號: pythontesting 微信公眾號:pythontesting