<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          架構(gòu)設(shè)計(jì)|性能優(yōu)化的十種手段(中篇)

          共 3488字,需瀏覽 7分鐘

           ·

          2022-09-29 19:10

          上一篇架構(gòu)設(shè)計(jì)|性能優(yōu)化的十種手段(上篇),我們總結(jié)了六種普適的性能優(yōu)化方法,包括索引、壓縮、緩存、預(yù)取、削峰填谷批量處理,簡單講解了每種技術(shù)手段的原理和實(shí)際應(yīng)用。

          在開啟最后一篇前,我們先需要搞清楚:在程序運(yùn)行期間,時(shí)間和空間都耗在哪里了?

          本文轉(zhuǎn)載信息如下:

          作者:code2life
          鏈接:https://code2life.top/2020/08/14/0056-performance2/

          時(shí)間都去哪兒了?

          人眨一次眼大約100毫秒,而現(xiàn)代1核CPU在一眨眼的功夫就可以執(zhí)行數(shù)億條指令。

          現(xiàn)代的CPU已經(jīng)非常厲害了,頻率已經(jīng)達(dá)到了GHz級(jí)別,也就是每秒數(shù)十億個(gè)指令周期。

          即使一些CPU指令需要多個(gè)時(shí)鐘周期,但由于有流水線機(jī)制的存在,平均下來大約每個(gè)時(shí)鐘周期能執(zhí)行1條指令,比如一個(gè)3GHz頻率的CPU核心,每秒大概可以執(zhí)行20億到40億左右的指令數(shù)量。

          程序運(yùn)行還需要RAM,也可能用到持久化存儲(chǔ),網(wǎng)絡(luò)等等。隨著新的技術(shù)和工藝的出現(xiàn),這些硬件也越來越厲害,比如CPU高速緩存的提升、NVMe固態(tài)硬盤相對(duì)SATA盤讀寫速率和延遲的飛躍等等。這些硬件具體有多強(qiáng)呢?

          有一個(gè)非常棒的網(wǎng)站“Latency Numbers Every Programmer Should Know”,可以直觀地查看從1990年到現(xiàn)在,高速緩存、內(nèi)存、硬盤、網(wǎng)絡(luò)時(shí)間開銷的具體數(shù)值。

          https://colin-scott.github.io/personal_website/research/interactive_latency.html

          下圖是2020年的截圖,的確是“每個(gè)開發(fā)者應(yīng)該知道的數(shù)字”。

          這里有幾個(gè)非常關(guān)鍵的數(shù)據(jù):

          • 存取一次CPU多級(jí)高速緩存的時(shí)間大約1-10納秒級(jí)別;
          • 存取一次主存(RAM)的時(shí)間大概在100納秒級(jí)別;
          • 固態(tài)硬盤的一次隨機(jī)讀寫大約在10微秒到1毫秒這個(gè)數(shù)量級(jí);
          • 網(wǎng)絡(luò)包在局域網(wǎng)傳輸一個(gè)來回大約是0.5毫秒。

          看到不同硬件之間數(shù)量級(jí)的差距,就很容易理解性能優(yōu)化的一些技術(shù)手段了。

          比如一次網(wǎng)絡(luò)傳輸?shù)臅r(shí)間,是主存訪問的5000倍,明白這點(diǎn)就不難理解寫for循環(huán)發(fā)HTTP請(qǐng)求,為什么會(huì)被扣工資了。

          放大到我們?nèi)菀赘兄臅r(shí)間范圍,來理解5000倍的差距:如果一次主存訪問是1天的話,一趟局域網(wǎng)數(shù)據(jù)傳輸就要13.7年。

          如果要傳輸更多網(wǎng)絡(luò)數(shù)據(jù),每兩個(gè)網(wǎng)絡(luò)幀之間還有固定的間隔(Interpacket Gap),在間隔期間傳輸Idle信號(hào),數(shù)據(jù)鏈路層以此來區(qū)分兩個(gè)數(shù)據(jù)包,具體數(shù)值在鏈接Wiki中有,這里截取幾個(gè)我們熟悉的網(wǎng)絡(luò)來感受一下:

          • 百兆以太網(wǎng): 0.96 μs
          • 千兆以太網(wǎng):96 ns
          • 萬兆以太網(wǎng):9.6 ns

          不過,單純看硬件的上限意義不大,從代碼到機(jī)器指令中間有許多層抽象,僅僅是在TCP連接上發(fā)一個(gè)字節(jié)的數(shù)據(jù)包,從操作系統(tǒng)內(nèi)核到網(wǎng)線,涉及到的基礎(chǔ)設(shè)施級(jí)別的軟硬件不計(jì)其數(shù)。到了應(yīng)用層,單次操作耗時(shí)雖然沒有非常精確的數(shù)字,但經(jīng)驗(yàn)上的范圍也值得參考:

          • 用Memcached/Redis存取緩存數(shù)據(jù):1-5 ms
          • 執(zhí)行一條簡單的數(shù)據(jù)庫查詢或更新操作:5-50ms
          • 在局域網(wǎng)中的TCP連接上收發(fā)一趟數(shù)據(jù)包:1-10ms;廣域網(wǎng)中大約10-200ms,視傳輸距離和網(wǎng)絡(luò)節(jié)點(diǎn)的設(shè)備而定
          • 從用戶態(tài)切換到內(nèi)核態(tài),完成一次系統(tǒng)調(diào)用:100ns - 1 μs,視不同的系統(tǒng)調(diào)用函數(shù)和硬件水平而定,少數(shù)系統(tǒng)調(diào)用可能遠(yuǎn)超此范圍。

          空間都去哪兒了?

          在計(jì)算機(jī)歷史上,非易失存儲(chǔ)技術(shù)的發(fā)展速度超過了摩爾定律。除了嵌入式設(shè)備、數(shù)據(jù)庫系統(tǒng)等等,現(xiàn)在大部分場景已經(jīng)不太需要優(yōu)化持久化存儲(chǔ)的空間占用了,這里主要講的是另一個(gè)相對(duì)稀缺的存儲(chǔ)形式 —— RAM,或者說主存/內(nèi)存。

          以JVM為例,在里面有很多我們創(chuàng)建的對(duì)象(Object)。

          • 每個(gè)Object都有一個(gè)包含Mark和類型指針的Header,占12個(gè)字節(jié)
          • 每個(gè)成員變量,根據(jù)數(shù)據(jù)類型的不同占不同的字節(jié)數(shù),如果是另一個(gè)對(duì)象,其對(duì)象指針占4個(gè)字節(jié)
          • 數(shù)組會(huì)根據(jù)聲明的大小,占用N倍于其類型Size的字節(jié)數(shù)
          • 成員變量之間需要對(duì)齊到4字節(jié),每個(gè)對(duì)象之間需要對(duì)齊到8字節(jié)

          如果在32G以上內(nèi)存的機(jī)器上,禁用了對(duì)象指針壓縮,對(duì)象指針會(huì)變成8字節(jié),包括Header中的Klass指針,這也就不難理解為什么堆內(nèi)存超過32G,JVM的性能直線下降了。

          舉個(gè)例子,一個(gè)有8個(gè)int類型成員的對(duì)象,需要占用48個(gè)字節(jié)(12+32+4),如果有十萬個(gè)這樣的Object,就需要占用4.58MB的內(nèi)存了。這個(gè)數(shù)字似乎看起來不大,而實(shí)際上一個(gè)Java服務(wù)的堆內(nèi)存里面,各種各樣的對(duì)象占用的內(nèi)存通常比這個(gè)數(shù)字多得多,大部分內(nèi)存耗在char[]這類數(shù)組或集合型數(shù)據(jù)類型上。

          堆內(nèi)存之外,又是另一個(gè)世界了。

          從操作系統(tǒng)進(jìn)程的角度去看,也有不少耗內(nèi)存的大戶,不管什么Runtime都逃不開這些空間開銷:每個(gè)線程需要分配MB級(jí)別的線程棧,運(yùn)行的程序和數(shù)據(jù)會(huì)緩存下來,用到的輸入輸出設(shè)備需要緩沖區(qū)……

          代碼“寫出來”的內(nèi)存占用,僅僅是冰山之上的部分,真正的內(nèi)存占用比“寫出來”的要更多,到處都存在空間利用率的問題。

          比如,即使我們?cè)贘ava代碼中只是寫了 response.getWriter().print(“OK”),給瀏覽器返回2字節(jié),網(wǎng)絡(luò)協(xié)議棧的層層封裝,協(xié)議頭部不斷增加的額外數(shù)據(jù),讓最終返回給瀏覽器的字節(jié)數(shù)遠(yuǎn)超原始的2字節(jié),像IP協(xié)議的報(bào)頭部就至少有20個(gè)字節(jié),而數(shù)據(jù)鏈路層的一個(gè)以太網(wǎng)幀頭部至少有18字節(jié)。

          如果傳輸?shù)臄?shù)據(jù)過大,各層協(xié)議還有最大傳輸單元MTU的限制,IPv4一個(gè)報(bào)文最大只能有64K比特,超過此值需要分拆發(fā)送并在接收端組合,更多額外的報(bào)頭導(dǎo)致空間利用率降低(IPv6則提供了Jumbogram機(jī)制,最大單包4G比特,“浪費(fèi)”就減少了)。

          這部分的“浪費(fèi)”有多大呢?下面的鏈接有個(gè)表格,傳輸1460個(gè)字節(jié)的載荷,經(jīng)過有線到無線網(wǎng)絡(luò)的轉(zhuǎn)換,至少再添120個(gè)字節(jié),空間利用率<92.4%。

          https://en.wikipedia.org/wiki/Jumbo_frame

          這種現(xiàn)象非常普遍,使用抽象層級(jí)越高的技術(shù)平臺(tái),平臺(tái)提供高級(jí)能力的同時(shí),其底層實(shí)現(xiàn)的“信息密度”通常越低。像Java的Object Header就是使用JVM的代價(jià),而更進(jìn)一步使用動(dòng)態(tài)類型語言,要為靈活性付出空間的代價(jià)則更大。哈希表的自動(dòng)擴(kuò)容,強(qiáng)大的反射能力等等,背后也付出了空間的代價(jià)。

          再比如,二進(jìn)制數(shù)據(jù)交換協(xié)議通常比純文本協(xié)議更加節(jié)約空間。但多數(shù)廠家我們?nèi)匀挥肑SON、XML等純文本協(xié)議,用信息的冗余來換取可讀性。即便是二進(jìn)制的數(shù)據(jù)交互格式,也會(huì)存在信息冗余,只能通過更好的協(xié)議和壓縮算法,盡量去逼近壓縮的極限 —— 信息熵。

          結(jié)語

          理解了時(shí)間和空間的消耗在哪后,還不能完全解釋軟件為何傾向于耗盡硬件資源。有一條定律可以解釋,正是它錘爆了摩爾定律。

          它就是安迪-比爾定律

          “安迪給什么,比爾拿走什么”。

          安迪指的是Intel前CEO安迪·葛洛夫,比爾指的是比爾·蓋茨。這句話的意思就是:軟件發(fā)展比硬件還快,總能吃得下硬件。20年前,在最強(qiáng)的計(jì)算機(jī)也不見得可以玩賽車游戲;10年前,個(gè)人電腦已經(jīng)可以玩畫質(zhì)還可以的3D賽車游戲了;現(xiàn)在,自動(dòng)駕駛+5G云駕駛已經(jīng)快成為現(xiàn)實(shí)。在這背后,是無數(shù)的硬件技術(shù)飛躍,以及吃掉了這些硬件的各類軟件。這也是我們每隔兩三年都要換手機(jī)的原因:不是機(jī)器老化變卡了,是嗜血的軟件在作怪。

          因此,即使現(xiàn)代的硬件水平已經(jīng)強(qiáng)悍到如此境地,性能優(yōu)化仍然是有必要的。軟件日益復(fù)雜,抽象層級(jí)越來越高,就越需要底層基礎(chǔ)設(shè)施被充分優(yōu)化。對(duì)于大部分開發(fā)者而言,高層代碼逐步走向低代碼化、可視化,“一行代碼”能產(chǎn)生的影響也越來越大,寫出低效代碼則會(huì)吃掉更多的硬件資源

          以上就是本文的全部內(nèi)容,如果覺得還不錯(cuò)的話歡迎點(diǎn)贊,轉(zhuǎn)發(fā)關(guān)注,感謝支持。


          參考:

          • https://colin-scott.github.io/personal_website/research/interactive_latency.html
          • https://en.wikipedia.org/wiki/Interpacket_gap
          • https://www.baeldung.com/java-memory-layout
          • https://tools.ietf.org/html/rfc791
          • https://en.wikipedia.org/wiki/Andy_and_Bill%27s_law

          推薦閱讀:

          瀏覽 80
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  性爱視频一区二区三区 | 亚洲AV无码成人精品一区色欲 | 欧美成人操比 | 最新中文字幕在线观看 | 一区二区视频在线播放 |