性能優(yōu)化系列:每個(gè)程序員都應(yīng)該知道的數(shù)字
前言
交流群里最常聽到的一句話就是:我項(xiàng)目太垃圾了,面試怎么辦。說實(shí)話,我聽了也感同身受,因?yàn)槲乙彩沁@么走過來的。而且,幾乎大部分人都會(huì)經(jīng)歷這個(gè)過程。所以,不多說了,安排。
之所以挑性能優(yōu)化這個(gè)方向,主要有幾個(gè)原因:
1)性能優(yōu)化是我很感興趣的一個(gè)方向,同時(shí)在過去幾年,我在這個(gè)方向上積累了一些經(jīng)驗(yàn),可以說,我之前的面試,項(xiàng)目上幾乎是靠性能優(yōu)化一招走遍天下。因此,我覺得可以拿出來和大家分享一下。
2)性能優(yōu)化非常通用,幾乎對(duì)于所有線上項(xiàng)目都可以適用,大家掌握了之后,立馬可以到項(xiàng)目中實(shí)踐起來。我想,應(yīng)該不存在不需要性能優(yōu)化的項(xiàng)目,除非你的項(xiàng)目只有“Hello world”。
3)性能優(yōu)化大部分內(nèi)容非常簡(jiǎn)單,幾乎沒有門檻,經(jīng)驗(yàn)較淺的同學(xué)也很容易上手,同時(shí)性能優(yōu)化也適用二八原則:掌握20%的內(nèi)容,足以解決80%的問題。
4)性能優(yōu)化很容易拿到結(jié)果,稍微有經(jīng)驗(yàn)點(diǎn)的同學(xué)應(yīng)該知道,做需求最怕拿不到結(jié)果,性能優(yōu)化就不一樣了,都是很直白的數(shù)字。1小時(shí)的任務(wù),我優(yōu)化成5分鐘,性能提升就是十來倍,簡(jiǎn)單粗暴。
不多說了,開懟。
正文
文章的標(biāo)題來源于 Jeff Dean 在谷歌的內(nèi)部一次分布式系統(tǒng)的演講,英文標(biāo)題為:Numbers Everyone Should Know。
這些數(shù)字與我們后續(xù)做性能優(yōu)化息息相關(guān),因此我將這部分內(nèi)容放在第一篇,幫助大家建立基本的性能概念。
先來看 Jeff Dea n 所說的數(shù)字是哪些:
注:1μs = 1000ns、1ms = 1000 μ s
操作 |
耗時(shí)/延遲 |
耗時(shí)*10億 |
一級(jí)緩存讀?。↙1) |
0.5ns |
0.5s |
分支錯(cuò)誤預(yù)測(cè) |
5ns |
5s |
二級(jí)緩存讀?。↙2) |
7ns |
7s |
互斥鎖的加鎖解鎖 |
25ns |
25s |
內(nèi)存尋址 |
100ns |
100s |
Zippy壓縮1KB數(shù)據(jù) |
3000ns( 3 μs ) | 50min |
在1Gbps網(wǎng)絡(luò)上發(fā)送1KB數(shù)據(jù) |
10,000ns( 10 μ s ) |
2.8h |
從SSD(1GB/s)隨機(jī)讀取4KB數(shù)據(jù) |
150,000ns( 15 0μs ) | 1.7days |
從內(nèi)存順序讀取1MB數(shù)據(jù) |
250,000ns( 250μs ) |
2.9days |
數(shù)據(jù)包在同數(shù)據(jù)中心一個(gè)往返 | 500,000ns ( 500μs ) | 5.8days |
從SSD (1GB/s) 順序讀取1MB數(shù)據(jù) |
1,00 0,000ns ( 1ms ) | 11.6days |
磁盤尋道 |
10,00 0,000ns( 10 ms ) |
3.8months |
從磁盤順序讀取1MB數(shù)據(jù) |
20,00 0,000ns(20ms) |
7.9months |
數(shù)據(jù)包從美國到荷蘭一個(gè)往返 |
150,00 0,000ns(150ms) |
4.75years |
表格 第三列將耗時(shí)數(shù)據(jù)提升10億倍,換算成大家更容易看的單位。
這份數(shù)據(jù)的最初來源為 Peter Norvig 的文章 :Teach Yourself Programming in Ten Years,地址:http://norvig.com/21-days.html。
伯克利 的 Colin Scott 根據(jù)這份 數(shù)據(jù),通過一定的算法,制作了一個(gè)可以根據(jù)時(shí)間的推移而變化的網(wǎng)站,地址為:https://colin-scott.github.io/personal_website/research/interactive_latency.html,源碼中注釋有詳細(xì)解釋計(jì)算邏輯,例如網(wǎng)絡(luò)帶寬是按每2年增加1倍,DRAM帶寬按每3年增加一倍。
根據(jù) Colin Scott 的圖表來看,到2021年,網(wǎng)絡(luò)帶寬、內(nèi)存、SSD、磁盤,都有數(shù)量級(jí)的提升,而 CPU 相關(guān)的一二級(jí)緩存變化不大,有興趣的可以自己點(diǎn)進(jìn)去看一看。
看這些數(shù)據(jù)的目的
首先,這些數(shù)據(jù)肯定不是完全準(zhǔn)確的,受限于眾多環(huán)境因素的影響,其實(shí)很難有所謂的準(zhǔn)確數(shù)字。
我們看這些數(shù)據(jù)更多是了解每個(gè)操作的耗時(shí)量級(jí),各個(gè)操作之間的數(shù)量級(jí)比率,從而對(duì)于我們工作中接觸到的一些相關(guān)知識(shí)有初步的概念。
而我將這個(gè)數(shù)據(jù)放在性能優(yōu)化系列文章的開篇,主要想先傳達(dá)給各位同學(xué)幾個(gè)概念:
1)CPU非常非???/h4>
CPU執(zhí) 行大部分簡(jiǎn)單指令只需要1個(gè)時(shí)鐘周期,我用個(gè)人電腦測(cè)試時(shí),CPU可以睿頻到 4.40GHz(見第2點(diǎn)的測(cè)試圖),也就是說此時(shí)執(zhí)行一個(gè)簡(jiǎn)單指令需要的時(shí)間大約是1/4.4ns,也就是0.23ns(納秒)。
這是什么概念了,舉個(gè)簡(jiǎn)單的例子,即使是真空中傳播的光,在0.23 ns 內(nèi)也只能走不到7厘米。
2)內(nèi)存很快了,但是相比CPU來說還是太慢了
CPU和內(nèi)存之間的瓶頸通常稱為馮·諾伊曼瓶頸。具體差別有多大了,我用自己的電腦做了個(gè)簡(jiǎn)單的測(cè)試。
我電腦 是 今年剛買的,硬件應(yīng)該都還比較新,但是配置比較普通,僅供參考。
CPU配置是 11th Gen Intel Core i5-11400F@2.60GHz,睿頻4.40GHz,測(cè)試結(jié)果看也確實(shí)跑到了4.40GHz了, 內(nèi)存配置是 DDR 4 3 2 00MHz。
測(cè)試結(jié)果如下圖所示:
從上圖看,內(nèi)存的讀取速度為41GB/s,感覺還是挺快的,但是L1 Cache為3TB/s,一比較,相差還是很大。
如果CPU按4.40GHz來算,執(zhí)行一個(gè)簡(jiǎn)單指令需要的時(shí)間大約是0.23ns(納秒),而內(nèi)存的延遲是88.7ns,相當(dāng)于CPU去內(nèi)存里取一個(gè)字節(jié),需要等待386個(gè)周期,可以看出,內(nèi)存相較于CPU來說,確實(shí)太慢了。
這也是為什么引入了L1、L2、L3緩存的原因,不過這邊我們不深入去研究這些東西,只是對(duì)CPU和內(nèi)存的性能差距有個(gè)大概概念。
3)磁盤性能非常非常慢
這個(gè)大家估計(jì)大家都知道,具體有多慢了, 我這邊在用自 己的電腦做了個(gè)簡(jiǎn)單的測(cè)試。
我電腦剛好有兩塊硬盤,一塊256GB的SSD(固態(tài)硬盤),一塊1T的HDD(機(jī)械硬盤)。
SSD測(cè)試結(jié)果如下圖所示:
忽略隊(duì)列(Q)和線程(T)的影響,順序讀(SEQ)的性能為1535.67MB/s,隨機(jī)讀(RND)的性能為49.61MB/s。
對(duì)比下上面內(nèi)存的性能 41GB/s ,盡管是SSD,性能還是存在數(shù)量級(jí)的差距,另一個(gè)就是隨機(jī)讀的性能相比順序讀也是存在數(shù)量級(jí)的差距。
HDD測(cè)試結(jié)果如下圖所示:
忽 略隊(duì)列(Q)和線程(T)的影響,順序讀(SEQ)的性能為183.49MB/s,隨機(jī)讀(RND)的性能為0.6MB/s。
對(duì)比下上面SSD的性能:順序讀 1535.67 比183.49,存在一個(gè)數(shù)量級(jí)的差距,隨機(jī)讀 49.61 比0.6,存在兩個(gè)數(shù)量級(jí)的差距。
而HDD順序讀和隨機(jī)讀的性能差距相比SSD就比較嚴(yán)重了,大概有300倍。簡(jiǎn)直慘不忍睹,不過相信現(xiàn)在的服務(wù)器應(yīng)該基本都是SSD了。如果發(fā)現(xiàn)自己公司服務(wù)器的磁盤還是HDD,那就趕緊溜吧。
4)磁盤順序I/O比隨機(jī)讀I/O快很多
這個(gè)在上面的測(cè)試也看出來了,都是數(shù)量級(jí)上的差距,特別是在以前的HDD上。有不少技術(shù)就是利用了順序I/O性能好的特點(diǎn)來提升性能,典型的有:kafka順序?qū)懴ⅰeveldb和RocksDB底層使用的LSM-Tree等。
5)網(wǎng)絡(luò)傳輸也是比較耗時(shí)的,基本都是毫秒級(jí)別
在開始的表格中可以看到,在同數(shù)據(jù)中心一個(gè)往返,需要0.5ms。
如果 是跨城市就更久了 , 這個(gè) 相信也不難理解,畢竟 信 號(hào) 要順著網(wǎng)線爬,距離越遠(yuǎn),當(dāng)然所需時(shí)間就越久了 。
下圖是上海到一些城市進(jìn)行PING操作所需的時(shí)間,可以看到張家口已經(jīng)需要30ms左右了,這也差不多就是北上的延遲。
這也是為什么我們?cè)诜?wù)器的路由策略上通常會(huì)優(yōu)先使用同機(jī)房?jī)?yōu)先、同中心優(yōu)先的策略。
這讓我想到我之前碰到的一個(gè)問題,當(dāng)時(shí)是一個(gè)新服務(wù)在測(cè)試,數(shù)據(jù)庫基本沒數(shù)據(jù),測(cè)試場(chǎng)景也是很簡(jiǎn)單的增刪改查,但是接口的性能就是很差,動(dòng)不動(dòng)就上百毫秒。
仔 細(xì)看了調(diào)用鏈后,發(fā)現(xiàn)每次DB操作都需要30ms左右,看了下機(jī)房分布后 ,發(fā)現(xiàn)是應(yīng)用服務(wù)器和 數(shù)據(jù)庫 服務(wù)器跨城市了,一個(gè)在北京一個(gè)在上海,導(dǎo)致會(huì)有固定30ms左右的延遲 。將兩者換到同機(jī)房后,基本就是1ms了。
總結(jié)
本文著重介紹了業(yè)務(wù)開發(fā)在做性能優(yōu)化需要掌握的一些核心概念,之所以放在最先介紹,是因?yàn)樵谖易鲂阅軆?yōu)化的過程中,發(fā)現(xiàn)絕大多數(shù)性能問題都是由于網(wǎng)絡(luò)I/O和磁盤I/O引起的。 對(duì)這些概念心中有數(shù)后 ,有利于我們更快的定位出性能瓶頸,從而更快的解決問題。