Python貓薦書系列之五:Python高性能編程

来源:https://www.cnblogs.com/pythonista/archive/2019/01/13/10263854.html
-Advertisement-
Play Games

稍微關心編程語言的使用趨勢的人都知道,最近幾年,國內最火的兩種語言非 Python 與 Go 莫屬,於是,隔三差五就會有人問:這兩種語言誰更厲害/好找工作/高工資…… 對於編程語言的爭論,就是猿界的生理周期,每個月都要鬧上一回。到了年末,各類榜單也是特別抓人眼球,鬧得更凶。 其實,它們各有對方所無法 ...


 

稍微關心編程語言的使用趨勢的人都知道,最近幾年,國內最火的兩種語言非 Python 與 Go 莫屬,於是,隔三差五就會有人問:這兩種語言誰更厲害/好找工作/高工資……

對於編程語言的爭論,就是猿界的生理周期,每個月都要鬧上一回。到了年末,各類榜單也是特別抓人眼球,鬧得更凶。

其實,它們各有對方所無法比擬的優勢以及用武之地,很多爭論都是沒有必要的。身為一個正在努力學習 Python 的(準)中年程式員,我覺得吧,先把一門語言精進了再說。沒有差勁的語言,只有差勁的程式員,等真的把語言學好了,必定是“山重水複疑無路,柳暗花明又一村”。

鋪墊已了,進入今天的正題,Python 貓薦書系列之五——

 

Python高性能編程

 

本書適合已入門 Python、還想要進階和提高的讀者閱讀。

所有電腦語言說到底都是在硬體層面的數據操作,所以高性能編程的一個終極目標可以說是“高性能硬體編程”。然而,Python 是一門高度抽象的電腦語言,它的一大優勢是開發團隊的高效,不可否認地存在這樣或那樣的設計缺陷,以及由於開發者的水平而造成的人為的性能缺陷。

本書的一大目的就是通過介紹各種模塊和原理,來促成在快速開發 Python 的同時避免很多性能局限,既減低開發及維護成本,又收穫系統的高效。

1、性能分析是基礎

首先的一個關鍵就是性能分析,藉此可以找到性能的瓶頸,使得性能調優做到事半功倍。

性能調優能夠讓你的代碼能夠跑得“足夠快”以及“足夠瘦”。性能分析能夠讓你用最小的代價做出最實用的決定。

書中介紹了幾種性能分析的工具:

(1)基本技術如 IPython 的 %timeit 魔法函數、time.time()、以及一個計時修飾器,使用這些技術來瞭解語句和函數的行為。

(2)內置工具如 cProfile,瞭解代碼中哪些函數耗時最長,並用 runsnake 進行可視化。

(3)line_profiler 工具,對選定的函數進行逐行分析,其結果包含每行被調用的次數以及每行花費的時間百分比。

(4)memory_profiler 工具,以圖的形式展示RAM的使用情況隨時間的變化,解釋為什麼某個函數占用了比預期更多的 RAM。

(5)Guppy 項目的 heapy 工具,查看 Python 堆中對象的數量以及每個對象的大小,這對於消滅奇怪的記憶體泄漏特別有用。

(6)dowser 工具,通過Web瀏覽器界面審查一個持續運行的進程中的實時對象。

(7)dis 模塊,查看 CPython 的位元組碼,瞭解基於棧的 Python 虛擬機如何運行。

(8)單元測試,在性能分析時要避免由優化手段帶來的破壞性後果。

作者強調了性能分析的重要性,同時也對如何確保性能分析的成功提了醒,例如,將測試代碼與主體代碼分離、避免硬體條件的干擾(如在BIOS上禁用了TurboBoost、禁用了操作系統改寫SpeedStep、只使用主電源等)、運行實驗時禁用後臺工具如備份和Dropbox、多次實驗、重啟並重跑實驗來二次驗證結果,等等。

性能分析對於高性能編程的作用,就好比複雜度分析對於演算法的作用,它本身不是高性能編程的一部分,但卻是最終有效的一種評判標準。

2、數據結構的影響

高性能編程最重要的事情是瞭解數據結構所能提供的性能保證。

高性能編程的很大一部分是瞭解你查詢數據的方式,並選擇一個能夠迅速響應這個查詢的數據結構。

書中主要分析了 4 種數據結構:列表和元組就類似於其它編程語言的數組,主要用於存儲具有內在次序的數據;而字典和集合就類似其它編程語言的哈希表/散列集,主要用於存儲無序的數據。

本書在介紹相關內容的時候很剋制,所介紹的都是些影響“速度更快、開銷更低”的內容,例如:內置的 Tim 排序演算法、列表的 resize 操作帶來的超額分配的開銷、元組的記憶體滯留(intern機制)帶來的資源優化、散列函數與嗅探函數的工作原理、散列碰撞帶來的麻煩與應對、Python 命名空間的管理,等等。

散列碰撞的結果散列碰撞的結果

理解了這些內容,就能更加瞭解在什麼情況下使用什麼數據結構,以及如何優化這些數據結構的性能。

另外,關於這 4 種數據結構,書中還得出了一些有趣的結論:對於一個擁有100 000 000個元素的大列表,實際分配的可能是112 500 007個元素;初始化一個列表比初始化一個元組慢5.1 倍;字典或集合預設的最小長度是8(也就是說,即使你只保存3個值,Python仍然會分配 8 個元素)、對於有限大小的字典不存在一個最佳的散列函數。

3、矩陣和矢量計算

矢量計算是電腦工作原理不可或缺的部分,也是在晶元層次上對程式進行加速所必須瞭解的部分。

然而,原生 Python 並不支持矢量操作,因為 Python 列表存儲的不是實際的數據,而是對實際數據的引用。在矢量和矩陣操作時,這種存儲結構會造成極大的性能下降。比如,grid[5][2] 中的兩個數字其實是索引值,程式需要根據索引值進行兩次查找,才能獲得實際的數據。

同時,因為數據被分片存儲,我們只能分別對每一片進行傳輸,而不是一次性傳輸整個塊,因此,記憶體傳輸的開銷也很大。

減少瓶頸最好的方法是讓代碼知道如何分配我們的記憶體以及如何使用我們的數據進行計算。

Numpy 能夠將數據連續存儲在記憶體中並支持數據的矢量操作,在數據處理方面,它是高性能編程的最佳解決方案之一。

Numpy 帶來性能提升的關鍵在於,它使用了高度優化且特殊構建的對象,取代了通用的列表結構來處理數組,由此減少了記憶體碎片;此外,自動矢量化的數學操作使得矩陣計算非常高效。

Numpy 在矢量操作上的缺陷是一次只能處理一個操作。例如,當我們做 A * B + C 這樣的矢量操作時,先要等待 A * B 操作完成,並保存數據在一個臨時矢量中,然後再將這個新的矢量和 C 相加。

Numexpr 模塊可以將矢量表達式編譯成非常高效的代碼,可以將緩存失效以及臨時變數的數量最小化。另外,它還能利用多核 CPU 以及 Intel 晶元專用的指令集來將速度最大化。

書中嘗試了多種優化方法的組合,通過詳細的分析,展示了高性能編程所能帶來的性能提升效果。

4、編譯器

書中提出一個觀點:讓你的代碼運行更快的最簡單的辦法就是讓它做更少的工作。

編譯器把代碼編譯成機器碼,是提高性能的關鍵組成部分。

不同的編譯器有什麼優勢呢,它們對於性能提升會帶來多少好處呢?書中主要介紹瞭如下編譯工具:

  • Cython ——這是編譯成C最通用的工具,覆蓋了Numpy和普通的Python代碼(需要一些C語言的知識)。
  • Shed Skin —— 一個用於非Numpy代碼的,自動把Python轉換成C的轉換器。
  • Numba —— 一個專用於Numpy代碼的新編譯器。
  • Pythran —— 一個用於Numpy和非numpy代碼的新編譯器。
  • PyPy —— 一個用於非Numpy代碼的,取代常規Python可執行程式的穩定的即時編譯器。

書中分析了這幾種編譯器的工作原理、優化範圍、以及適用場景等,是不錯的入門介紹。此外,作者還提到了其它的編譯工具,如Theano、Parakeet、PyViennaCL、ViennaCL、Nuitka 與 Pyston 等,它們各有取捨,在不同領域提供了支撐之力。

5、密集型任務

高性能編程的一個改進方向是提高密集型任務的處理效率,而這樣的任務無非兩大類:I/O 密集型與 CPU 密集型。

I/O 密集型任務主要是磁碟讀寫與網路通信任務,占用較多 I/O 時間,而對 CPU 要求較少;CPU 密集型任務恰恰相反,它們要消耗較多的 CPU 時間,進行大量的複雜的計算,例如計算圓周率與解析視頻等。

改善 I/O 密集型任務的技術是非同步編程 ,它使得程式在 I/O 阻塞時,併發執行其它任務,並通過“事件迴圈”機制來管理各項任務的運行時機,從而提升程式的執行效率。

書中介紹了三種非同步編程的庫:Gevent、Tornado 和 Asyncio,對三種模塊的區別做了較多分析。

改善 CPU 密集型任務的主要方法是利用多核 CPU 進行多進程的運算。

Multiprocessing 模塊使用基於進程和基於線程的並行處理,在隊列上共用任務,以及在進程間共用數據,是處理 CPU 密集型任務的重要技術。

書中沒有隱瞞它的局限性:Amdahl 定律揭示的優化限度、適應於單機多核而多機則有其它選擇、全局解釋鎖 GIL 的束縛、以及進程間通信(同步數據和檢查共用數據)的開銷。針對進程間通信問題,書中還分析了多種解決方案,例如 Less Naïve Pool、Manager、Redis、RawValue、MMap 等。

6、集群與現場教訓

集群是一種多伺服器運行相同任務的結構,也就是說,集群中的各節點提供相同的服務,其優點是系統擴展容易、具備容災恢復能力。

集群需要剋服的挑戰有:機器間信息同步的延遲、機器間配置與性能的差異、機器的損耗與維護、其它難以預料的問題。書中列舉了兩個慘痛的教訓:華爾街公司騎士資本由於軟體升級引入的錯誤,損失4.62億美元;Skype 公司 24 小時全球中斷的嚴重事故。

書中給我們重點介紹了三個集群化解決方案:Parallel Python、IPython Parallel 和 NSQ。引申也介紹了一些普遍使用的方案,如 Celery、Gearman、PyRes、SQS。

關於現場教訓,它們不僅僅是一些事故或者故事而已,由成功的公司所總結出來的經驗更是來之不易的智慧。書中單獨用一章內容分享了六篇文章,這些文章出自幾個使用 Python 的公司/大型組織,像是Adaptive Lab、RadimRehurek、Smesh、PyPy 與 Lanyrd ,這些國外組織的一線實踐經驗,應該也能給國內的 Python 社區帶來一些啟示。

7、寫在最後

眾所周知,Python 應用前景大、簡單易學、方便開發與部署,然而與其它編程語言相比,它的性能幾乎總是落於下風。如何解決這個難題呢?本期薦書的書目就是一種回應。

《Python高性能編程》全書從微觀到巨集觀對高性能編程的方方面面做了講解,主要包含以下主題:電腦內部結構的背景知識、列表和元組、字典和集合、迭代器和生成器、矩陣和矢量計算、編譯器、併發、集群和工作隊列等。這些內容為編寫更快的 Python 指明瞭答案。

本篇文章主要以梳理書中的內容要點為主,平均而兼顧地理清了全書脈絡(PS:介紹得太面面俱到了,但願不被指責為一篇流水賬的讀書筆記才好……)。我認為,鑒於書中談及的這些話題,它就足以成為我們薦書欄目的一員了。除去某些句段的糟糕翻譯、成書時間比較早(2014年)而造成的過時外,這本書總體質量不錯,可稱為是一份優秀的高性能編程的指引手冊。

關於薦書欄目,我最後多說幾句。本欄目原計劃兩周左右出一篇,但由於其它系列文章花費了我不少時間,而要寫好一篇薦書/書評也特別費勁,最後生生造成了現在兩月一更的尷尬局面……這篇文章是個錯誤的示範,我不該試圖全面通讀與概括其內容的。因此,我決定今後選一些易讀的書目,在寫作上也儘量走短小精悍風,希望能持續地將本欄目運作下去。若你有什麼建議(如書目推薦、書評推薦、寫作建議、甚至是投稿),我隨時歡迎,先行致謝啦。

往期薦書回顧:
第一期:《編寫高質量代碼改善 Python 程式的 91 個建議
第二期:《Python最佳實踐指南
第三期:《黑客與畫家
第四期:《Python源碼剖析

-----------------

本文原創並首發於微信公眾號【Python貓】,後臺回覆“愛學習”,免費獲得20+本精選電子書。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 終於組裝了臺電腦,花了三千塊。Ryzen5 2600+GTS250,8G DDR4駭客神條,240G SSD。幾秒鐘就能開機,開機完立刻就能打開軟體,還能同時開很多軟體,徹底顛覆了之前我對電腦的印象。 拿到新電腦肯定刷洛谷啊,之前都沒時間刷。結果新手村就卡住了(忘記怎麼不開double四捨五入)。這 ...
  • 背景:聽說Java運行時環境的記憶體劃分是挺進BAT的必經之路。 記憶體劃分: Java程式記憶體的劃分是交由JVM執行的,而不像C語言那樣需要程式員自己買單(C語言需要程式員為每一個new操作去配對delete/free代碼),放權給JVM虛擬機處理有利也有弊,好處是不容易出現記憶體泄漏和記憶體溢出問題,壞 ...
  • [toc] 1.環境介紹 本篇文章涉及到前面文章的工程,mirco service provider、mirco service consumer以及需要另外新建一個工程mirco service turbine hystrix dashbord。 2.服務監控 2.1 加入依賴 為“mirco s ...
  • jdk8 foreach創建對象優化 lambda foreach 創建對象 @Async public void asyncFullEsDoc() { List<Integer> docIdList = Arrays.asList(913,914); if (CollectionUtil.isNo ...
  • 背景 Elasticsearch 不像關係型資料庫,沒有簡易的 SQL 用來查詢數據,只能通過調用 RESTful API 實現查詢。大體上查詢分為兩種,基於 URL 的和基於請求主體的。基於 URL 的方式比較簡單清晰,用得較多,在這簡單記錄下。 基本模式 基於 URL 的搜索請求基本模式如上圖所 ...
  • 看完上圖你是什麼反應?會罵人嗎?會就對了……,代碼整潔之道,是一條很漫長的路,註釋是其中一部分。 ...
  • Exercise2是註釋和井號 Comments and Pound Characters 具體詳情請參考 "習題一" ,這裡就不在做過多的贅述。 習題 3: 數字和數學計算 學習目標: 瞭解Python中常用的算術運算符,並瞭解運算符之間的先後運算順序 在各大常用的電腦語言中都有常見的算術運算符 ...
  • 這篇筆記依然是在做《Python語言程式設計》第5章迴圈的習題。其中有兩類問題需要記錄下來。 第一是如何畫圍棋棋盤。圍棋棋盤共有19縱19橫。其中,位於(0,0)的星位叫天元,其餘8個星位坐標分別是:(-6,6),(0,6),(6,6),(-6,0),(6,0),(-6,-6),(0,-6),(6, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...