Go語言高效編程:原理、可觀測性與優(yōu)化 [波蘭]巴特洛米·普洛特卡
定 價:139 元
雖然技術在進步,硬件也越來越物美價廉,但如今軟件工程師仍需要關注程序的性能優(yōu)化。本書將介紹軟件效率問題、Go語言快速入門知識、如何實現(xiàn)高效編程,告訴大家如何高效進行性能優(yōu)化,以及何時進行,并給出需要的工具和相關知識,讓你使用較少的資源實現(xiàn)高效編程。
本書將帶你穿梭于現(xiàn)代軟件架構與操作系統(tǒng)中CPU與內存的奇妙世界,從Go的基礎知識到高級特性與優(yōu)化妙招,將被一一揭曉。書中還會介紹針對特定用例的優(yōu)化之道,以及各種可觀測工具,比如度量指標、日志、追蹤和用于效率提升的大殺器性能剖析(Profiling)。最后,從商業(yè)邏輯視角來看軟件優(yōu)化的最佳時機,確保所有的努力既實際又高效。
前言軟件開發(fā)一直都是一個求真務實的領域,軟件工程師追求的往往是極高的性能目標、需 求與效率的平衡、優(yōu)雅而高效的代碼,以及易于閱讀、擴展和維護的代碼庫。實現(xiàn)這些真的可能嗎?是的,筆者將通過本書展示如何實現(xiàn)這些!本書在某些方面是獨樹一幟的,因為它不是 一本速成教程,而是一本關于編寫高效且務實的軟件的完整指南,涵蓋了筆者在軟件開發(fā)領域的所見所聞。通過本書,你將學到很多關于Go語言編程以及如何對其進行優(yōu)化的知識。不要被本書的書名所迷惑,雖然筆者使用Go語言作為示例語言來展示優(yōu)化思維和可觀測性模式, 但本書的11章中有8章與編程語言無關。這些技術理念是相通的,可以用來改進任何其他語言(例如,Java 、C#、Scala 、Python 、C 、Rust 或 Haskell)編寫的軟件。如果你期待的是一本包含大量底層優(yōu)化技巧的書,那么這本書并不適合你。首先,軟件的性能優(yōu)化并不能一概而論。某人通過展開循環(huán)或在其結構體字段中使用指針獲得了更 好的效率,并不意味著可以推廣到所有場景!本書將介紹一些優(yōu)化技巧,但會著重強調 效率的重要性。其次,一些低級且有風險的技巧通常并沒有多大用處。在大多數(shù)情況下,只需了解程序中浪費CPU時間和資源的癥結所在,就可以低成本、高效率地實現(xiàn)效率和可擴展性目標。此外,你將了解到,除了用C 、Rust或匯編語言重寫程序,在原程序基礎上也存在高效的解決方案!在閱讀本書正文之前,建議先閱讀本書的前言,其中有本書的寫作目的,以及筆者專注于效率這個主題的原因。你還可以學習到如何更好地使用本書,從而在軟件開發(fā)任務中受益。緣何而作筆者花了大約1200小時撰寫本書,撰寫本書并非一時沖動。在社交媒體盛行的時代,閱讀與寫作紙質書給人一種過時的感覺,但以筆者的經(jīng)驗來看,現(xiàn)代媒體往往將信息過 度簡化,因為它們全靠短平快的內容來吸引讀者,并獲得收益,因此讀者看過之后會覺 得似是而非。這會導致不良誘導( https://oreil.ly/A8dCv),且與筆者想通過本書實現(xiàn)的目 標相沖突。筆者的目標其實很簡單讓自己所使用或依賴的軟件變得更好,并實現(xiàn)以下目的:? 希望軟件的貢獻者(Contributor)和維護者(Maintainer)能夠知曉其代碼的效率,通 過持續(xù)優(yōu)化讓它變得更加高效。? 希望軟件的貢獻者和維護者能夠審慎對待所有的拉取請求(pull request),并提高效率。? 希望周圍的人知道如何專業(yè)地處理性能問題,而不是營造緊張氛圍。? 希望用戶和利益相關者對目前行業(yè)中風靡的基準測試和廉價營銷保持謹慎之心。? 希望領導者、主管和產(chǎn)品經(jīng)理能夠充分地處理軟件效率問題,并合理制定可落地的效率規(guī)范,以幫助工程師交付出合格的產(chǎn)品。筆者想對軟件的可持續(xù)發(fā)展盡一些綿薄之力。每一次因為軟件的效率問題而浪費的CPU時間和內存不僅浪費了公司的大量金錢,也浪費了能源和硬件,從而對環(huán)境產(chǎn)生了嚴重的影響。因此,在省錢并保護環(huán)境的同時為你的企業(yè)創(chuàng)造更大的價值,是一件非常有意義的事。筆者認為寫一本書是實現(xiàn)這一目標的最佳方式。這會比通過日常工作、開源項目和會議 傳播得更廣。希望更多的人能夠看到本書。創(chuàng)作背景在撰寫本書時,筆者已經(jīng)從大量的實踐和錯誤中積累了經(jīng)驗,在軟件效率和開發(fā)高質量軟件方面小有所成。筆者在19歲時就開始了全職專業(yè)軟件開發(fā)的職業(yè)生涯,曾在英特爾從事軟件定義基礎設施(SDI)工作,同時還攻讀了全日制計算機科學專業(yè)。筆者的技術棧最初是使 用Python為OpenStack項目( https://www.openstack.org)編寫代碼,然后使用 C 在 Mesosphere (https://oreil.ly/yUHzn)和 Twitter(現(xiàn) X)與一些出色工程師合作編寫代碼[包括對當時流行的 Mesos (https://mesos.apache.org )項目的貢獻]。最近幾年,轉向 為 Kubernetes (https://kubernetes.io)開發(fā) Go 程序,并愛上了這門語言。筆者在英特爾花了不少時間來研究節(jié)點超額訂閱功能( https://oreil.ly/uPnb7)。通常來 講,超額訂閱允許在單臺機器上運行比平時更多的程序。這樣做是可行的,因為從統(tǒng)計學上來看,所有程序幾乎很少會同時使用所有的保留資源。但從筆者目前的認知來看, 通過優(yōu)化軟件來省錢通常比使用這樣的復雜算法更容易、更有效。2016 年,筆者搬到倫敦,為一家初創(chuàng)游戲公司工作,與谷歌、亞馬遜、微軟和 Facebook 的一些前員工合作開發(fā)和運營一個全球游戲平臺。我們用Go開發(fā)了許多微服務,主要是在全球數(shù)十個 Kubernetes集群上運行。正是在這里,筆者學到了很多有關分布式系統(tǒng)、站點可靠性工程和監(jiān)控的知識。也許正是在那時,筆者習慣了使用一些出色的可觀測性工具,這對于軟件效率的落地至關重要。本書第6章中會對此進行說明。從此以后,一切順理成章。筆者將對可觀測性方面的熱情轉化為了開發(fā)和使用一個名為Prometheus(https://prometheus.io)的流行開源時間序列數(shù)據(jù)庫,以用于監(jiān)控目的。最終, 筆者有幸成為一名官方維護者,同時啟動了多個Go開源項目和庫。最后,筆者也借此機會與 Fabian Reinartz 在開源社區(qū)中共同創(chuàng)建了一個名為Thanos(https://thanos.io)的 大型分布式時間序列數(shù)據(jù)庫。這兩個項目目前的使用范圍已經(jīng)非常之廣,它們很有可能 正在你的公司的基礎設施中運行!2019 年,筆者加入了Red Hat,全職從事可觀測性系統(tǒng)的開源工作。也因此有了更多深入研究持續(xù)性能剖析( continuous profiling)解決方案的機會,本書正文中也會提到這 一點。此外,筆者還擔任了云原生計算基金會( Cloud Native Computing Foundation ,CNCF,網(wǎng)址為 https://cncf.io)的大使和可觀測性技術咨詢小組(TAG,網(wǎng)址為 https://oreil.ly/f9UYG)的技術負責人,與其他成員共同組織會議。通過Prometheus和 Thanos項目, 筆者的團隊每年都會通過CNCF指導計劃(CNCF Mentoring Initiatives,網(wǎng)址為 https:// oreil.ly/rU0bg)指導多名工程師。筆者已經(jīng)編寫或審查了成千上萬行生產(chǎn)級的代碼,使其具備可靠性和擴展性。到目前為止,筆者累計指導了20多位工程師。然而,最令筆者印象深刻的是主持開源工作,因為在這項工作中,筆者有機會與世界各地不同公司、具有不同背景、目標和需求的人士互動。總的來說,筆者和共事的優(yōu)秀人員取得了一些驚人的成就。在一個高質量代碼環(huán)境中工作是幸運的,因為這樣就不會花費時間去優(yōu)化一些延遲或風格問題。我們致力于良好的 系統(tǒng)設計、代碼可維護性和可讀性,并試圖將這些價值觀帶入開源社區(qū)。但是,如果筆者有機會再次開發(fā)像Thanos 這樣的項目,一定會改善一件事:嘗試更多地關注代碼質量和所選擇的算法的效率。筆者會從一開始就專注于制定更清晰的效率規(guī)范,并在基準 測試和性能剖析上投入更多的精力。不要誤會,筆者這樣說并不表示 Thanos 的性能/效率差,相反,目前Thanos比一些競 爭對手更快,而且使用的資源也更少。我們在這上面花了不少時間,但仍然有優(yōu)化空間,有許多瓶頸等待社區(qū)關注與反饋。如果從一開始就應用本書所介紹的知識、技術和 建議,筆者相信我們可以將開發(fā)成本降低一半,甚至更多,讓Thanos達到今天的狀態(tài)。這些經(jīng)歷讓筆者頓悟寫一本這樣的書是多么有必要!隨著全民編程時代的到來,那些沒有計算機科學背景的開發(fā)者可能會遇到很多錯誤和誤解,尤其是在軟件效率方面。市面 上沒有太多文獻可以為效率或擴展問題提供參考,尤其是對于Go這樣的年輕語言而言更是如此。希望本書的問世能提供一些指導和幫助。本書受眾本書旨在提供必要的工具和知識,以便根據(jù)具體情況和組織目標來明確何時以及如何應 用效率優(yōu)化。因此,本書的主要受眾是使用Go語言和任何其他現(xiàn)代編程語言編寫程序的軟件開發(fā)者。軟件工程師的本職工作應該是確保他們創(chuàng)建的軟件能在特定的功能需求 和效率要求下工作。所以在開始閱讀本書時最好具備一些基本的編程技能。筆者相信本書對那些操作他人編寫的軟件的人也有用,例如,DevOps工程師、SRE、系統(tǒng)管理員和平臺團隊。按照一些優(yōu)化設計層級(如3.5節(jié)中所述),有時對軟件優(yōu)化進行投資是有意義的,而有時可能需要在其他層級去解決問題!此外,為了實現(xiàn)可靠的效 率,軟件工程師必須對生產(chǎn)環(huán)境進行大量基準測試和實驗(如第7章中所述),這通常意 味著與平臺團隊的密切協(xié)作。第6章中介紹的可觀測性是目前軟件工程中最為推薦的先 進工具。筆者強烈建議應該避免為SRE區(qū)分應用程序性能監(jiān)控(APM)和可觀測性。如果你在哪里看到了這種區(qū)分,極有可能是某些云服務商又要忽悠你付費。正如本書中將 要介紹的,可以在所有軟件可觀測性中復用相同的工具、插樁和信號,因為它們同宗 同源,有著相同的目標構建更好的軟件產(chǎn)品!最后,筆者向那些希望保持技術能力,并想了解如何確保團隊不會在效率問題上浪費數(shù)百萬美元的經(jīng)理、產(chǎn)品經(jīng)理和領導者隆重推薦本書!內容組織本書共分為11章。在第1章中,將討論軟件效率及其重要性;在第2章中,將簡要介紹Go及其在效率方面的考量;在第3章中,將討論優(yōu)化以及如何思考和處理優(yōu)化。提高效率可能需要花費大量時間,但系統(tǒng)性的方法可以幫助你節(jié)省大量時間和精力。在第4章和第5章中,將解釋延遲、CPU和內存資源,以及操作系統(tǒng)和Go如何將它們抽象化。從第6章開始,將繼續(xù)討論圍繞軟件效率進行數(shù)據(jù)驅動決策的含義;在第7章中,將討論復雜度分析和實驗的可靠性;在第8章和第9章中,將解釋基準測試和性能剖析技術。最后兩章也很重要。在第10章中,將展示不同優(yōu)化情況的各種示例;在第11章中,將介紹一些經(jīng)驗教訓,并總結在Go社區(qū)中看到的各種有關效率的模式和技巧。
巴特洛米·普洛特卡是Red Hat的一名首席軟件工程師,擁有可觀測性和SRE背景。他是一位CNCF大使以及TAGO可觀測技術的負責人,也是Thanos項目的聯(lián)合創(chuàng)始人。他還作為核心維護人員參與了包括Prometheus和bingo在內的許多使用Go語言編寫的開源項目。
目錄前言1第1章 軟件效率很重要91.1 性能的深意111.2 效率的常見誤區(qū)141.2.1 誤區(qū)1:優(yōu)化后的代碼可讀性差141.2.2 誤區(qū)2:YAGNI原則201.2.3 誤區(qū)3:硬件變得更快、更廉價221.2.4 誤區(qū)4:使用水平擴展291.2.5 誤區(qū)5:盡快投入市場321.3 效率優(yōu)先351.4 本章小結36第2章 初識Go語言372.1 Go基礎知識382.1.1 Go語言形態(tài)382.1.2 Go簡化代碼庫392.1.3 Go是谷歌的開源項目402.1.4 核心設計原則412.1.5 包和模塊422.1.6 Go如何管理依賴432.1.7 一致的工具462.1.8錯誤處理472.1.9語言生態(tài)502.1.10移除未使用的import和變量512.1.11單元測試和表測試522.2 高級語言元素542.2.1做好代碼文檔542.2.2向后兼容性和可移植性562.2.3 Go運行時572.2.4面向對象編程582.2.5泛型612.3 Go快嗎642.4 本章小結66第3章 征服效率683.1 對優(yōu)化的思考693.1.1 合理的優(yōu)化703.1.2 慎重的優(yōu)化723.2 優(yōu)化挑戰(zhàn)743.3 了解你的目標763.3.1 應該形式化效率要求783.3.2 資源感知的效率要求803.3.3 獲取和評估效率目標833.3.4 定義RAER的示例843.4 合理解決效率問題873.5 優(yōu)化設計層級903.6 效率感知的開發(fā)流程943.6.1功能階段953.6.2效率階段973.7 本章小結99第4章 Go如何使用CPU資源1014.1 現(xiàn)代計算機架構中的CPU1024.2 匯編語言1044.3 了解Go編譯器1074.4 CPU和內存墻問題1134.4.1 分層緩存系統(tǒng)1154.4.2 流水線和亂序執(zhí)行1164.4.3 超線程1184.5 調度器1204.5.1 操作系統(tǒng)調度器1204.5.2 Go運行時調度器1244.6 何時使用并發(fā)1304.7 本章小結131第5章 Go如何使用內存資源1335.1 內存的相關背景1345.2 是否有內存問題1355.3 物理內存1365.4 操作系統(tǒng)內存管理機制1395.4.1 虛擬內存1405.4.2 mmap系統(tǒng)調用1445.4.3 操作系統(tǒng)內存映射1495.5 Go內存管理機制1525.5.1 值、指針和內存塊1565.5.2 Go內存分配器1605.5.3 垃圾收集1645.6 本章小結169第6章 效率可觀測性1706.1 可觀測性1716.2 針對延遲的插樁示例1746.2.1 日志1756.2.2 追蹤1806.2.3 指標1856.3 效率指標語義1916.3.1 延遲1926.3.2 CPU使用情況1986.3.3 內存使用情況2026.4 本章小結205第7章 數(shù)據(jù)驅動的效率評估2067.1 復雜度分析2077.1.1 效率復雜度預估2077.1.2 漸近復雜度的近似預估2097.1.3復雜度分析實踐2127.2 基準測試的藝術2157.2.1與功能測試的對比2177.2.2基準測試謊言2197.3 實驗的可靠性2207.3.1人為錯誤2217.3.2重現(xiàn)生產(chǎn)環(huán)境2227.3.3性能的不確定性2247.4 基準測試級別2297.4.1生產(chǎn)環(huán)境中的基準測試2317.4.2宏基準測試2317.4.3微基準測試2327.4.4 正確使用基準測試2337.5 本章小結235第8章 基準測試實踐2368.1 微基準測試詳述2368.1.1 Go基準測試2388.1.2 基準測試結果分析2438.2 微基準測試技巧2478.2.1結果方差分析2478.2.2 確定工作流程2478.2.3確保結果正確2498.2.4分享基準測試2518.2.5 進行不同的基準測試2548.2.6微基準測試與內存管理2568.2.7 微基準測試與編譯器優(yōu)化2578.3 宏基準測試詳述2618.3.1基礎知識2628.3.2 Go e2e 框架2658.3.3理解并觀測結果2708.4 常見的宏基準測試工作流程2778.5本章小結279第9章 數(shù)據(jù)驅動的瓶頸分析2809.1 效率根因分析2819.2 Go中的性能剖析2819.2.1 pprof格式2829.2.2 go tool pprof 報告2909.3 捕獲性能剖析信號3029.4 常用性能剖析插樁3069.4.1 堆3079.4.2 goroutine3109.4.3 CPU3129.4.4 off-CPU時間3149.5 技巧3179.5.1 共享性能剖析3179.5.2 持續(xù)性能剖析3189.5.3 比較和聚合性能剖析3229.6 本章小結323第10章 優(yōu)化示例32410.1 Sum示例32510.2 優(yōu)化延遲32610.2.1 優(yōu)化bytes.Split32810.2.2 優(yōu)化runtime.slicebytetostring33010.2.3 優(yōu)化strconv.Parse33310.3 優(yōu)化內存使用33510.3.1 轉向流式算法33610.3.2 優(yōu)化bufio.Scanner33710.4 使用并發(fā)優(yōu)化延遲34210.4.1 樸素的并發(fā)34210.4.2 分布式工作者模式34410.4.3 不需要協(xié)調的工作者模式(任務分片)34610.4.4 流式、分片的工作者模式34810.5 額外技巧:打破常規(guī)思維35110.6 本章小結352第11章 優(yōu)化模式35311.1 常見模式35411.1.1 減少工作量35411.1.2 以功能換效率35611.1.3 以空間換時間35611.1.4 以時間換空間35811.2 3R優(yōu)化法35811.2.1 減少分配35811.2.2 重用內存35911.2.3 回收36011.3 不要泄漏資源36211.3.1 控制goroutine的生命周期36411.3.2 可靠地關閉資源37011.3.3 耗盡資源37311.4 盡可能預分配37611.5 使用數(shù)組時過度使用內存38011.6 內存重用和池化38311.7 本章小結39111.8 后續(xù)計劃393附錄 延遲的粗略估算396