-
-
Notifications
You must be signed in to change notification settings - Fork 596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add traditional Chinesse version #66
Open
johnnychhsu
wants to merge
10
commits into
dgryski:master
Choose a base branch
from
johnnychhsu:add_traditional_Chinese
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
ed7c862
Add traditional Chinesse version
johnnychhsu db976b8
Add translation for first section
johnnychhsu 6001e80
Add translation for the first section
johnnychhsu 03002b7
Finish the first section
johnnychhsu b8e9672
Add more and modify for review
johnnychhsu 942ebad
Add more translation
johnnychhsu 8924d8f
Add more translation
johnnychhsu fdb597f
Add more translation
johnnychhsu 80e45b7
Fix for review comments
johnnychhsu ac6403f
Finish the first section
johnnychhsu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# 優化你的 Go 程式 | ||
|
||
這本書會告訴你如何去寫高效能的Go程式碼。 | ||
|
||
雖然書中有部分章節,會討論如何優化分散式系統中個別的服務(像是cache等等),但是如何去設計一個高效能的分散式系統超出了本書的範圍。網路上可以找到許多關於分散式系統的優質文章,優化分散式系統涵蓋了完全不同的領域、不同的研究與設計之間的取捨。 | ||
|
||
**所有的內容都受到CC-BY-SA license的保護** | ||
|
||
本書分成幾個主要章節: | ||
1. 不寫出慢(爛?)程式的基本觀念 | ||
* 寫程式的基本技巧 | ||
2. 如何寫出運行速度快的程式 | ||
* 針對Go的部分,教你如何妥善利用Go來寫出好程式 | ||
3. 寫出運行速度極快的程式的進階技巧 | ||
* 當我們優化完後,還是覺得不夠快、不夠好時的進階觀念 | ||
|
||
以上三個章節,可以各自濃縮、提煉成以下要點: | ||
1. "合理化" | ||
2. "精確化" | ||
3. "高風險化" | ||
|
||
## 該在何時、何處開始優化 | ||
|
||
因為這很重要,所以我要~~說三次~~開門見山的說清楚:我們真的需要做優化這件事嗎? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 標題跟內文空一行 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 謝謝! |
||
|
||
優化是需要成本的。一般來說,可以把程式的複雜度或是工程師在寫程式時的腦袋負荷(cognitive load)當作是優化的成本。優化後的程式碼,幾乎都比沒有優化之前還要難懂。 | ||
|
||
另一方面,我們也需要考慮優化的經濟效益(economics of optimization)。身為一個軟體工程師,我們的時間是寶貴的,我們也必須考慮做優化這件事的機會成本:像是修產品的bug,開發新的feature等等。優化固然有趣,但不見得是該做的事。產品的效率當然重要,但完成進度並交付、保持程式的正確性等,這些事情也是不容忽視的部分。 | ||
|
||
選擇最需要優化的部分來優化。有時候,不見得是要增加CPU的使用效率,反而是增進使用者體驗(UX),像是簡單地增加一個進度條,或是先把畫面畫出來(rendering),再把一些耗時的運算放在背景跑等等。 | ||
|
||
有時候會有很明顯需要優化的地方:一個只需要一小時的事情卻花了三小時。想當然爾這邊優化後絕對會比沒有來得好。 | ||
|
||
若是單純地因為好優化而優化,並不值得。比起投入的時間,不如忽略這些小地方會更好。把這當成是更加妥善利用*你的*時間。我們需要選擇何時、何處去優化,可以看成是在“高效的軟體”和“高效的開發”之間,去找一個平衡點。 | ||
|
||
“過早的優化是萬惡之源”,相信大家都聽過這句名言。但是,我們卻忽略了這句話所代表的真正意義 | ||
|
||
> "軟體工程師浪費許多時間在考慮、擔心程式中瑣碎、不重要的地方的效能。考慮到後續的維持、除錯,嘗試去改善這些地方反而會帶來負面的結果。我們應該忽略這些細小的改進,一言以敝之:有97%的情形是不需要去做無謂的優化的(過早的優化是萬惡之源)。當然,我們不能忽略真正需要改進,那剩下的3%" | ||
> | ||
> -- <cite>Knuth</cite> | ||
|
||
補充: https://www.youtube.com/watch?time_continue=429&v=3WBaY61c9sE | ||
* 簡單的優化還是要做的,不要忽略 | ||
* 對演算法、資料結構有更多的理解,會讓我們更容易發現這些簡單的優化 | ||
|
||
我們應該優化嗎? | ||
> 是的,但只有當這個程式很重要卻很慢,而且我們是可以在維持程式的正確性、穩定性和簡潔易讀的前提之下,讓他更快的時候 | ||
> | ||
> -- <cite>The Practice of Programming, Kernighan and Pike</cite> | ||
|
||
過早的優化可能會讓我們陷於特定的設計架構之中。通常,優化過的程式,比較不容易隨著需求改變而改動,也很難放棄(沉沒成本,指已投入且不可回收的成本)。 | ||
|
||
可以看看[BitFunnel效能預估](http://bitfunnel.org/strangeloop)中提到的trade-off,裡面有具體的數字可以參考。假設我們有一個搜尋引擎,總共需要30,000台機器,分別在不同的資料中心。每一台機器假設需要花費$1,000美金。如果我們可以讓每台機器的效率加倍,這樣一來每年可以替公司省下$1,500,000美金。就算讓一個工程師花一整年的時間只優化運算效率1%,都值得。 | ||
|
||
大多數時候,程式的大小、速度,並不是我們需要操心的議題。這種情況下,最好的優化就是不去花時間優化它。再來就乾脆買更好的機器讓他快。 | ||
|
||
如果你決定要開始優化你的程式,那就繼續往下讀吧! | ||
|
||
## 如何優化 | ||
|
||
### 優化流程 | ||
在進入細節之前,我們先來看看優化的基本流程 | ||
|
||
重構是一種優化的方法。但在重構的過程,除了在某些面向上改進了程式(重複的程式碼、簡潔度等等)、改善了效能(減少CPU、記憶體的使用量、降低了延遲等等),通常都伴隨著可讀性的降低。因此,除了要有單元測試來確保重構後的程式保有正確的邏輯,我們也需要好的評測來確保這些改動真的有優化我們的程式。有些時候,我們以為會增進效能的改動,實際上可能根本沒效果,或甚至降低了效能。若是如此,記得一定要改回原本的版本。 | ||
|
||
<cite>[What is the best comment in source code you have ever encountered? - Stack Overflow](https://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered)</cite>: | ||
<pre> | ||
// | ||
// Dear maintainer: | ||
// | ||
// Once you are done trying to 'optimize' this routine, | ||
// and have realized what a terrible mistake that was, | ||
// please increment the following counter as a warning | ||
// to the next guy: | ||
// | ||
// total_hours_wasted_here = 42 | ||
// | ||
</pre> | ||
|
||
我們必須使用具代表性的評測(benchmarks),它要能提供正確、可復現的結果。如果單次的測試具有很大的偏移量(variance),這會讓我們更難去評估進步幅度較小的優化。我們需要使用[benchstat](https://golang.org/x/perf/benchstat),或是具有相同統計效果的評測,而不能只是感覺好像變好了就好。我們需要寫下進行評測的步驟,如果在其中有用到自己寫的腳本或工具,也都要一併上傳到repo裡,並提供清楚說明的文檔,告知別人如何使用。需要注意的是,較大型的評測(包括評測資料本身及其工具),需要較多的時間來執行,也因此會讓開發迭代速度下降。 | ||
|
||
任何可以被測量的指標都可以被優化:因此,在優化前我們要確定我們的指標(像是CPU使用率、記憶體使用率等等)是正確的。 | ||
|
||
下一步,是決定我們要優化什麼。如果我們要優化的,是CPU的使用率,那我們可以接受的優化後執行速度是多少?是比現在快兩倍?十倍?我們可以把這個問題,定義成「處理大小為N的任務,花費了T時間」嗎?或是我們希望降低記憶體的使用量?那要降低多少?我們願意犧牲多少執行速度來換取較低的記憶體使用量?我們願意為了較低的記憶體需求犧牲什麼? | ||
|
||
優化降低網路服務(web service)的延遲是更困難的問題。坊間有一整本書都在解釋如何去對web server做效能測試。 | ||
會困難最主要的原因,在於單一的一個函數,做效能評測的結果通常不會都一樣。對於web service來說,我們不會只拿到一個數字,一個好的評測套件,通常會給出一個延遲分佈(latency distribution)。讀者可以參考這個演講["How NOT to Measure Latency" by Gil Tene](https://youtu.be/lJ8ydIuPFeU) | ||
|
||
TODO: 參考下面幾章關於優化網路服務的部分 | ||
|
||
優化的目標要很明確。我們幾乎總是可以讓某項指標更快一點、好一點。優化帶來的進步通常是遞減的,我們必須先想好什麼時候要停、願意付出多少的成本,來換取些許的進步、願意犧牲可讀性到什麼程度,來換取性能上的進步。 | ||
|
||
Dan Luu的演講[BitFunnel performance | ||
estimation](http://bitfunnel.org/strangeloop)提供了一個例子,示範如何大概的估算優化目標是否是合理的。Simon Eskildsen在SRECon的演講[Advanced Napkin Math: Estimating System Performance from First Principles](https://www.youtube.com/watch?v=IxkSlnrRFqc)針對這個主題更深入的解釋。 | ||
|
||
最後,Jon Bentley的書"Programming Pearls"有一章節在討論Fermi problems。可惜的是,由於微軟在1990年代和2000年代早期,把這些估算技巧當成面試考題(puzzle interview | ||
questions),讓大家對這些技巧有著不太好的印象。 | ||
|
||
對於從頭開始的專案,我們不應該到最後才考慮效能問題或到最後才進行評測。嘴上說說很容易,但如果效能是這個專案很關鍵的部分,那在初期設計整個架構的時候,我們就必須一起考慮這個問題。除此之外,在死線前一刻做任何重大的架構改變都是很危險的。 | ||
|
||
在開發的過程中,我們應該專注在如何透過合理的演算法、資料結構等來提升專案的效能。比較底層的優化,可以等到系統大致完成的時候再來處理。若是還沒完成整個專案,就對整個系統做分析(profiling),我們很有可能會得到偏頗的結論,導致我們花時間關注不是真正造成瓶頸的部分。 | ||
|
||
TODO:如何解決或避免因為不好的程式造成的"Death by 1000 cuts"問題? | ||
|
||
解:“過早的悲觀是萬惡的根源”(Premature pessimization is the root of all evil),呼應了我們說過的第一條法則:精確化。 | ||
我們不用讓每行程式碼都很快,但也不應該亂寫 | ||
|
||
> | ||
過早的悲觀指的是,因為多做了無謂的計算,我們把本來可以快的程式寫複雜、寫慢了 | ||
"Premature pessimization is when you write code that is slower than it needs to | ||
be, usually by asking for unnecessary extra work, when equivalently complex code | ||
would be faster and should just naturally flow out of your fingers." | ||
> | ||
> -- <cite>Herb Sutter</cite> | ||
|
||
因為摻雜了除了程式碼本身以外的其他因素,我們很難把評測作為持續整合(CI)的一部分,也很難制定評測的目標。比較好的做法,是讓工程師在適當的硬體上自己跑評測,並把評測結果放到提交記錄(commit message)裡。至於其他一般的提交,盡量在複審程式碼(code review)時用”肉眼“去判斷對效能的影響。 | ||
|
||
TODO: 如何追蹤效能變化? | ||
|
||
編寫可以被評測的程式碼,對整個大系統作效能分析。若想分析部分的程式碼,那部分程式碼必須可以被獨立抽取出來,並由工程師提供必要的相關背景資料(context),讓評測可以跑,並跑出具代表性的結果。 | ||
|
||
根據優化目標效能和當前的效能之間的差距,我們可以知道應該從哪裡下手。如果我們想要增進10%~20%的效能,我們可能可以透過一些coding技巧和簡單的修改來達成。如果我們想要的,是10倍以上的效能進步,這就不是簡單的把乘法改成左移運算就能完成的事。我們可能就需要把整個技術堆(technical stack)做改動,甚至是重新設計整個架構。 | ||
|
||
要設計具有好效能的系統,需要不同層面的知識,從系統設計、網路、硬體(CPU、快取、存儲等)、演算法、調整參數和除錯等等。在有限的時間內,考慮從哪個層面下手可以得到最好的成果:不一定總是要從演算法或是調整程式參數下手。 | ||
|
||
整體來說,優化最好從上到下來做。從系統層面來看,系統設計對整體效能的影響來說是最大的。我們要確保在適合的層面上來解決問題。 | ||
|
||
這本書大說是在講減少 CPU 使用率、減少記憶體的使用量和降低延遲。必須注意的是,我們不太可能一次做到全方面的優化。或許我們讓 CPU 使用率下降了,但同時卻讓記憶體使用量上升。也或許我們需要降低記憶體使用量,但也因此讓程式需要更多時間來完成。 | ||
|
||
[Amdahl's Law](https://en.wikipedia.org/wiki/Amdahl%27s_law)告訴我們要專注在優化瓶頸(bottlenecks)。假設我們讓只佔整體時間 5% 的部分加快了一倍,對於整體來說只進步了 2.5%。另一方面,如果我們讓了整體 80% 的部分進步了 10%,對於整體來說卻是進步了將近 8%。做分析(profiling)可以幫助我們判斷是哪個部分真正耗時。 | ||
|
||
優化的時候,我們希望可以減少 CPU 的工作量。快排比起泡泡排序法是更快的算法,因為快排需要的步驟比較少,是個更有效的演算法。透過使用更好算法,我們可以有效減少 CPU 的工作量來完成相同的任務。 | ||
|
||
調整程式的參數(tuning),像是優化編譯器,通常只會帶來小小的進步。改變演算法、資料結構,這種基本上改變程式架構或內容的方式,才比較會帶來顯著的進步。編譯器技術的確是會進步,但緩慢。[Proebsting's | ||
Law](http://proebsting.cs.arizona.edu/law.html)提到,編譯器效能每18年會進步一倍,和摩爾定律(稍微被誤解的版本)處理器的效能每18個月會進步一倍,明顯差很多。改善演算法可以帶來更大幅度的進展。混合整數的演算法,把[效能提升了 30,000 倍以上](https://agtb.wordpress.com/2010/12/23/progress-in-algorithms-beats-moore%E2%80%99s-law/)。 | ||
可以看看[這個](https://medium.com/@buckhx/unwinding-uber-s-most-efficient-service-406413c5871d),Uber 把一個地理空間相關的算法,從暴力解特化成更針對這個問題的解法。換一個編譯器是沒辦法給我們一樣的效果的。 | ||
|
||
TODO: 優化快速傅立葉轉換(FFT)浮點數運算,和 MMM 演算法的差異 | ||
|
||
透過分析程式(profiling),我們可以發現大部分時間耗在某個部分(routine)。他可能是個耗時的操作,也可能是個簡單的運算但被重複執行了好幾遍。我們可以先試試看能不能降低這部分的複雜度,或是減少被使用的次數,而不是立刻跳下去加速這部分。我們會在之後的章節介紹更多具體的優化策略。 | ||
|
||
動手前,我們先問問自己這三個問題: | ||
1. 我們真的需要做嗎?(最快的程式,是永遠不被執行的程式) | ||
2. 如果真的要做,這是最好的演算法嗎? | ||
3. 如果要做,這是這個演算法最好的實作方式嗎? | ||
|
||
## 優化技巧 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
建議中文跟英文之間都需要有空白,會比較好閱讀
請參考: https://github.com/sparanoid/chinese-copywriting-guidelines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
同意,謝謝你的建議跟分享!