怎么做好Java性能優(yōu)化
引言
性能優(yōu)化是一個(gè)很復(fù)雜的工作,且充滿了不確定性。
它不像Java業(yè)務(wù)代碼,可以一次編寫到處運(yùn)行(write once, run anywhere),往往一些我們可能并不能察覺的變化,就會(huì)帶來(lái)驚喜/驚嚇。
能夠全面的了解并評(píng)估我們所負(fù)責(zé)應(yīng)用的性能,我認(rèn)為是提升技術(shù)確定性和技術(shù)感知能力的非常有效的手段。
本文盡可能簡(jiǎn)短的總結(jié)我自己在性能優(yōu)化上面的一些體會(huì)和經(jīng)驗(yàn),從實(shí)踐的角度出發(fā)盡量避免過(guò)于啰嗦和生硬,但相關(guān)的知識(shí)實(shí)在太多,受限于個(gè)人經(jīng)驗(yàn)和技術(shù)深度,不足之外還請(qǐng)大家補(bǔ)充。
- 第1部分是偏背景類知識(shí)的介紹,有這方面知識(shí)的同學(xué)可以直接跳過(guò)。
一. 了解運(yùn)行環(huán)境
大多數(shù)的編程語(yǔ)言(尤其是Java)做了非常多的事情來(lái)幫助我們不用太了解硬件也能很容易的寫出正確工作的代碼,但你如果要全面了解性能,卻需要具備不少的從硬件、操作系統(tǒng)到軟件層面的知識(shí)。
1.1 服務(wù)器
目前我們大量使用Intel 64位架構(gòu)的Xeon處理器,除此之外還會(huì)有AMD x64處理器、ARM服務(wù)器處理器(如:華為鯤鵬、阿里倚天)、未來(lái)還會(huì)有RISC-V架構(gòu)的處理器、以及一些專用FPGA芯片等等。
我們這里主要聊聊目前我們大量使用的阿里云ECS使用的Intel 8269CY處理器。
1.1.1 處理器
Intel Xeon Platinum 8269CY,阿里云使用的這一款處理器是阿里定制款,并不能在Intel的官方手冊(cè)中查詢到,不過(guò)我們可以通過(guò)下方Intel處理器的命名規(guī)則了解到不少的信息,它是一款這樣的處理器:主頻2.5GHz(睿頻3.2GHz、最大睿頻3.8GHz),26核心52線程(具備超線程技術(shù)),6通道DDR4-2933內(nèi)存,最大配置內(nèi)存1T,Cascade Lake微架構(gòu),48通道PCI-E 3.0,14nm光刻工藝,205W TDP。
它是這一代至強(qiáng)處理器中性能比較強(qiáng)的型號(hào)了,最大支持8路部署。
具備動(dòng)態(tài)自動(dòng)超頻的能力將能夠短時(shí)間提升性能,同時(shí)在少數(shù)核心忙碌的時(shí)候還可以讓它們保持長(zhǎng)時(shí)間的自動(dòng)超頻,這會(huì)嚴(yán)重的影響我們對(duì)應(yīng)用性能的評(píng)估(少量測(cè)試時(shí)性能很好,大規(guī)模測(cè)試時(shí)下降很厲害)。
最大配置內(nèi)存1TB,代表處理器具備48bit的VA(虛擬地址),也就是通常需要四級(jí)頁(yè)表(下一代具備57bit VA的處理器已經(jīng)在設(shè)計(jì)中了,通常需要五級(jí)頁(yè)表),過(guò)深的頁(yè)表顯然是極大的影響內(nèi)存訪問(wèn)的性能以及占用內(nèi)存(頁(yè)表也是存儲(chǔ)在內(nèi)存中的)的,所以Intel設(shè)計(jì)了大頁(yè)(2MB、1GB大頁(yè))機(jī)制,以減少過(guò)深的頁(yè)表帶來(lái)的影響。
6通道2933MHz的內(nèi)存總線代表它具備總計(jì)約137GB/s(內(nèi)存總線是64bit位寬)的寬帶,不過(guò)需要記住他們是高度并行設(shè)計(jì)的。
這個(gè)微處理的CPU核架構(gòu)如下圖所示,采用8發(fā)射亂序架構(gòu), 32KB指令+32KB數(shù)據(jù) L1 Cache,1MB L2 Cache。
發(fā)射單元是CPU內(nèi)部真正的計(jì)算單元,它的多少是CPU性能的關(guān)鍵因素。8個(gè)發(fā)射單元中有4個(gè)單元都可以進(jìn)行基本整數(shù)運(yùn)算(ALU單元),只有2個(gè)可以進(jìn)行整數(shù)乘除和浮點(diǎn)運(yùn)算,所以對(duì)于大量浮點(diǎn)運(yùn)算的場(chǎng)景并行效率會(huì)偏低。
1個(gè)CPU核對(duì)應(yīng)的2個(gè)HT(這里指超線程技術(shù)虛擬出的硬件線程)是共享8個(gè)發(fā)射單元的,所以這兩個(gè)HT之間將會(huì)有非常大的相互影響(這也會(huì)導(dǎo)致操作系統(tǒng)內(nèi)CPU的使用率不再是線性值,具體請(qǐng)查閱相關(guān)資料),L1、L2 Cache同樣也是共享的,所以也會(huì)相互影響。
Intel Xeon處理器在分支預(yù)測(cè)上面花了很多功夫,所以在較多分支代碼(通常就是if else這類代碼)時(shí)性能往往也能做的很好,比大多數(shù)的ARM架構(gòu)做的都要好。
Java常用的指針壓縮技術(shù)也受益于x86架構(gòu)靈活的尋址能力(如:mov eax, ecx * 8 + 8),可以一條指令完成,同時(shí)也不會(huì)帶來(lái)性能的損失,但是這在ARM、RISC-V等RISC(精簡(jiǎn)指令集架構(gòu),Reduced Instruction Set Computing)處理器上就不適用了。
從可靠渠道了解道,下一代架構(gòu)(Sunny Cove)將大幅度的進(jìn)行架構(gòu)優(yōu)化,升級(jí)為10發(fā)射,同時(shí)L1 Cache將數(shù)據(jù)部分增加到48KB,這代表接下來(lái)的處理器將更加側(cè)重于提升SIMD(單指令多操作數(shù),Signle Instruction Multiple Data)等的數(shù)據(jù)計(jì)算性能。
一顆8269CY內(nèi)部有26個(gè)CPU核,采用如下的拓?fù)浣Y(jié)構(gòu)進(jìn)行連接。這一代處理器最多有28個(gè)CPU核,8269CY屏蔽掉了2個(gè)核心,以降低對(duì)產(chǎn)品良率的要求(節(jié)約成本)。
可以看到總計(jì)35.75MB的L3 Cache(圖中為L(zhǎng)LC: Last Level Cache)被分為了13塊(圖中是14塊,屏蔽2個(gè)核心的同時(shí)也屏蔽了與之匹配的L3 Cache),每塊2.75MB,并不是簡(jiǎn)單意義理解上的是一大塊統(tǒng)一的區(qū)域。
6通道的內(nèi)存控制器也分布在左右兩側(cè),與實(shí)際主板上內(nèi)存插槽的位置關(guān)系是對(duì)應(yīng)的。
這些信息都告訴我們,這是一顆并行能力非常強(qiáng)的多核心處理器。
1.1.2 服務(wù)器
阿里云通常都是采用雙Intel處理器的2U機(jī)型(基于散熱、密度、性價(jià)比等等的考慮),基本都是2個(gè)NUMA(非一致性內(nèi)存訪問(wèn),Non Uniform Memory Access)節(jié)點(diǎn)。
具體到Intel 8269CY,代表一臺(tái)服務(wù)器具備52個(gè)物理核心,104個(gè)硬件線程,通常阿里云會(huì)稱之為104核。
NUMA技術(shù)的出現(xiàn)是硬件工程師的妥協(xié)(他們實(shí)在沒有能力做到在多CPU的情況下還能實(shí)現(xiàn)訪問(wèn)任何地址的性能一致性),所以做的不好也會(huì)嚴(yán)重的降低性能,大多數(shù)情況下虛擬機(jī)/容器調(diào)度團(tuán)隊(duì)要做的是將NUMA打開,同時(shí)將一個(gè)虛擬機(jī)/容器部署到同一個(gè)NUMA節(jié)點(diǎn)上。
這幾年AMD的發(fā)展很好,它的多核架構(gòu)與Intel有很大的不同,不久的將來(lái)阿里云將會(huì)部署不少采用AMD處理器的機(jī)型。
AMD處理器的NUMA節(jié)點(diǎn)將會(huì)更多,而且拓?fù)潢P(guān)系也會(huì)更復(fù)雜,阿里自研的倚天(采用ARM架構(gòu))就更復(fù)雜了。這意味著虛擬機(jī)/容器調(diào)度團(tuán)隊(duì)夠得忙了。
多數(shù)情況下服務(wù)器大都采用CPU:內(nèi)存為1:2或1:4的配置,即配置雙Intel 8269CY的物理機(jī),通常都會(huì)配備192GB或384GB的內(nèi)存。
如果虛擬機(jī)/容器需要的內(nèi)存:CPU過(guò)大的情況下,將很難實(shí)現(xiàn)內(nèi)存在CPU對(duì)應(yīng)的NUMA節(jié)點(diǎn)上就近分配了,也就是說(shuō)性能就不能得到保證。
由于2U機(jī)型的物理高度是1U機(jī)型的2倍,所以有更多的空間放下更多的SSD盤、高性能PCI-E設(shè)備等。
不過(guò)云廠商肯定是不愿意直接將物理機(jī)賣給用戶(畢竟他們已經(jīng)不再是以前的托管物理機(jī)公司)的,再怎么也得在上面架一層,也就是做成ECS再賣給客戶,這樣諸如熱遷移、高可用等功能才能實(shí)現(xiàn)。
前述的"架一層"是通過(guò)虛擬化技術(shù)來(lái)實(shí)現(xiàn)的。
1.1.3 虛擬化技術(shù)
一臺(tái)物理機(jī)性能很強(qiáng)大,通常我們只需要里面的一小塊,但我們又希望不要感知到其他人在共享這臺(tái)物理機(jī),所以催生了虛擬化技術(shù)(簡(jiǎn)單來(lái)說(shuō)就是讓可以讓一個(gè)CPU工作起來(lái)就像多個(gè)CPU并行運(yùn)行,從而使得在一臺(tái)服務(wù)器內(nèi)可以同時(shí)運(yùn)行多個(gè)操作系統(tǒng))。
早期的虛擬機(jī)技術(shù)是通過(guò)軟件實(shí)現(xiàn)的,老牌廠商如VMWare,但是性能犧牲的有點(diǎn)多,硬件廠商也看好虛擬機(jī)技術(shù)的前景,所以便有了硬件虛擬化技術(shù)。
各廠商的實(shí)現(xiàn)并不相同,但差異不是很大,好在有專門的虛擬化處理模塊去兼容就可以了。
Intel的虛擬化技術(shù)叫Intel VT(Virtualization Technology),它包括VT-x(處理器的虛擬化支持)、VT-d(直接I/O訪問(wèn)的虛擬化)、VT-c(網(wǎng)絡(luò)連接的虛擬化),以及在網(wǎng)絡(luò)性能上的SR-IOV技術(shù)(Single Root I/O Virtualization)。
這里面一個(gè)很重要的事情是,原本我們?cè)L問(wèn)內(nèi)存的一層轉(zhuǎn)換(線性地址->物理地址)會(huì)變成二層轉(zhuǎn)換(VM內(nèi)線性地址->Host線性地址->物理地址),這會(huì)引入更多的內(nèi)存開銷以及頁(yè)表的轉(zhuǎn)換工作。
所以大多數(shù)云廠商會(huì)在Host操作系統(tǒng)上開啟大頁(yè)(Linux 操作系統(tǒng)通常是使用透明大頁(yè)技術(shù)),以減少內(nèi)存相關(guān)的虛擬化開銷。
服務(wù)器對(duì)網(wǎng)絡(luò)性能的要求是很高的,現(xiàn)在的網(wǎng)絡(luò)硬件都支持網(wǎng)卡多隊(duì)列技術(shù),通常情況下需要將VM中的網(wǎng)絡(luò)中斷分散給不同的CPU核來(lái)處理,以避免單核轉(zhuǎn)發(fā)帶來(lái)的性能瓶頸。
Host操作系統(tǒng)需要管理它上面的一個(gè)或多個(gè)VM(虛擬機(jī),Virtual Machine),以及前述提及的處理網(wǎng)卡中斷,這會(huì)帶來(lái)一定的CPU消耗。
阿里云上一代機(jī)型,服務(wù)器總計(jì)是96核(即96個(gè)硬件線程HT,實(shí)際是48個(gè)物理核),但最多只能分配出88核,需要保留8個(gè)核(相當(dāng)于物理機(jī)CPU減少8.3%)給Host操作系統(tǒng)使用,同時(shí)由于I/O相關(guān)的虛擬化開銷,整機(jī)性能會(huì)下降超過(guò)10%。
阿里云為了最大限度的降低虛擬化的開銷,研發(fā)了牛逼的“彈性裸金屬服務(wù)器 - 神龍”,號(hào)稱不但不會(huì)因?yàn)樘摂M化降低性能,反而會(huì)提升部分性能(主要是網(wǎng)絡(luò)轉(zhuǎn)發(fā))。
1.1.4 神龍服務(wù)器
為了避免虛擬化對(duì)性能的影響,阿里云(類似還有亞馬遜等云廠商的類似方案)研發(fā)了神龍服務(wù)器。
簡(jiǎn)單來(lái)說(shuō)就是設(shè)計(jì)了神龍MOC卡,將大部分虛擬機(jī)管理工作、網(wǎng)絡(luò)中斷處理等從CPU offload到MOC卡進(jìn)行處理。
神龍MOC卡是一塊PCI-E 3.0設(shè)備,其內(nèi)有專門設(shè)計(jì)的用于網(wǎng)絡(luò)處理的FPGA芯片,以及2顆低功耗的x86處理器(據(jù)傳是Intel Atom),最大限度的接手Host操作系統(tǒng)的虛擬化管理工作。
通過(guò)這樣的設(shè)計(jì),在網(wǎng)絡(luò)轉(zhuǎn)發(fā)性能上甚至能做到10倍于裸物理機(jī),做到了當(dāng)之無(wú)愧的裸金屬。
104核的物理機(jī)可以直接虛擬出一臺(tái)104核的超大ECS,再也不用保留幾個(gè)核心給Host操作系統(tǒng)使用了。
1.2 VPC
VPC(虛擬專有云,Virtual Private Cloud),大多數(shù)云上的用戶都希望自己的網(wǎng)絡(luò)與其它的客戶隔離,就像自建機(jī)房一樣,這里面最重要的是網(wǎng)絡(luò)虛擬化技術(shù),目前阿里云采用的是VxLAN協(xié)議,它底層采用UDP協(xié)議進(jìn)行數(shù)據(jù)傳輸,整體數(shù)據(jù)包結(jié)構(gòu)如下圖所示。
VxLAN在VxLAN幀頭中引入了類似VLAN ID的網(wǎng)絡(luò)標(biāo)識(shí),稱為VxLAN網(wǎng)絡(luò)標(biāo)識(shí)VNI(VxLAN Network ID),由24比特組成,理論上可支持多達(dá)16M的VxLAN段,從而滿足了大規(guī)模不同網(wǎng)絡(luò)之間的標(biāo)識(shí)、隔離需求。
這一層的引入將會(huì)使原始的網(wǎng)絡(luò)包增加50 Bytes的固定長(zhǎng)度的頭。當(dāng)然,還需要與之匹配的交換機(jī)、路由器、網(wǎng)關(guān)等等。
1.3 容器技術(shù)
虛擬化技術(shù)的極致優(yōu)化雖然已經(jīng)極大解決了VM層虛擬化的額外開銷問(wèn)題,但VM操作系統(tǒng)層的開銷是無(wú)法避免的,同時(shí)如今的Java應(yīng)用大多都可以做到單進(jìn)程部署,VM操作系統(tǒng)這一層的開銷顯得有一些浪費(fèi)(當(dāng)前,它換來(lái)了極強(qiáng)的隔離性和安全性)。
容器技術(shù)構(gòu)建于操作系統(tǒng)的支持,目前主要使用Linux操作系統(tǒng),容器最有名的是Docker,它是基于Linux 的 cgroup 技術(shù)構(gòu)建的。
VM的體驗(yàn)實(shí)在是太好了,所以容器的終極目標(biāo)就是具備VM的體驗(yàn)的同時(shí)還沒有VM操作系統(tǒng)層的開銷。在容器里,執(zhí)行top、free等命令時(shí),我們只希望看到容器視圖,同時(shí)網(wǎng)絡(luò)也是容器視圖,不得不排查問(wèn)題需要抓包時(shí)可以僅抓容器網(wǎng)絡(luò)的包。
目前阿里云ECS 16GB內(nèi)存的機(jī)型,實(shí)際上操作系統(tǒng)內(nèi)看到的可用內(nèi)存只有15GB,32GB的機(jī)型則只有30.75GB。
容器沒有這個(gè)問(wèn)題,因?yàn)閷?shí)際上在容器上運(yùn)行的任務(wù)僅僅是操作系統(tǒng)上的一個(gè)或多個(gè)進(jìn)程而已。
由于容器的這種邏輯隔離特性,所以不同企業(yè)的應(yīng)用基本上是不太可能部署到同一個(gè)操作系統(tǒng)上的(即同一臺(tái)ECS)。
容器最影響性能的點(diǎn)是容器是否超賣、是否綁核、核分配的策略等等,以及前述的眾多知識(shí)點(diǎn)都會(huì)對(duì)性能有不小的影響。
通常企業(yè)核心應(yīng)用都會(huì)要求綁核(即容器的多個(gè)vCPU的分布位置是確定以及專用的,同時(shí)還得考慮Intel HT、AMD CCD/CCX、NUMA等問(wèn)題),這樣性能的確定性才可以得到保證。
在Docker與VM之間,其實(shí)還存在別的更均衡的容器技術(shù),大多數(shù)公司稱之為安全容器,它采用了硬件虛擬化來(lái)實(shí)現(xiàn)強(qiáng)隔離,但并不需要一個(gè)很重的VM操作系統(tǒng)(比如 Linux),取而代之的是一個(gè)非常輕的微內(nèi)核(它僅支持實(shí)現(xiàn)容器所必須的部分內(nèi)核功能,同時(shí)大多數(shù)工作會(huì)轉(zhuǎn)發(fā)給Host操作系統(tǒng)處理)。
這個(gè)技術(shù)是云廠商很想要的,這是他們售賣可靠FaaS(功能即服務(wù),F(xiàn)unction as a Service)的基礎(chǔ)。
二. 獲取性能數(shù)據(jù)
進(jìn)行性能優(yōu)化前,我們需要做的是收集到足夠、準(zhǔn)確以及有代表性的性能數(shù)據(jù),分析性能瓶頸,然后才能進(jìn)行有效的優(yōu)化。
評(píng)估一個(gè)應(yīng)用的性能無(wú)疑是一件非常復(fù)雜的事,大多數(shù)情況下一個(gè)應(yīng)用會(huì)有很多個(gè)接口,且同一個(gè)接品會(huì)因?yàn)槿雲(yún)⒌牟煌蛘邇?nèi)部業(yè)務(wù)邏輯的不同帶來(lái)非常大的執(zhí)行邏輯的變化,所以我們得首先想清楚,我們到底是要優(yōu)化什么業(yè)務(wù)場(chǎng)景下的性能(對(duì)于訂單來(lái)說(shuō),也許就是下單)。
在性能測(cè)試用例跑起來(lái)后,怎么樣拿到我們想要的真實(shí)的性能數(shù)據(jù)就很關(guān)鍵了,因?yàn)橛^測(cè)者效應(yīng)的存在(指“觀測(cè)”這種行為對(duì)被觀測(cè)對(duì)象造成一定影響的效應(yīng),它在生活中極其常見),獲取性能數(shù)據(jù)的同時(shí)也會(huì)對(duì)被測(cè)應(yīng)用產(chǎn)生或多或少的影響,所以我們需要深入的了解我們所使用的性能數(shù)據(jù)獲取工具的工作原理。
具體到Java上(其它語(yǔ)言也基本是類似的),我們想知道一個(gè)應(yīng)用到底在做什么,主要有兩種手段:
- Instrumentation(代碼嵌入):
指的是可以用獨(dú)立于應(yīng)用程序之外的代理(Agent)程序來(lái)監(jiān)測(cè)運(yùn)行在JVM上的應(yīng)用程序,包括但不限于獲取JVM運(yùn)行時(shí)狀態(tài),替換和修改類定義等。
通俗點(diǎn)理解就是在函數(shù)的執(zhí)行前后插代碼,統(tǒng)計(jì)函數(shù)執(zhí)行的耗時(shí)。
了解基本原理后,我們大概會(huì)知道,這種方式對(duì)性能的影響是比較大的,函數(shù)越簡(jiǎn)短執(zhí)行的次數(shù)越多影響也會(huì)越大,不同它的好處也是顯而易見的:可以統(tǒng)計(jì)出函數(shù)的執(zhí)行次數(shù)以及不漏過(guò)任何一個(gè)細(xì)節(jié)。
這種方式一般用于應(yīng)用早期的優(yōu)化分析。
- Sampling(采樣):
采用固定的頻率打斷程序的執(zhí)行,然后拉取各線程的執(zhí)行棧進(jìn)行統(tǒng)計(jì)分析。
采樣頻率的大小決定了觀測(cè)結(jié)果的最小粒度和誤差,一些執(zhí)行次數(shù)較多的小函數(shù)可能會(huì)被統(tǒng)計(jì)的偏多,一些執(zhí)行次數(shù)較少的小函數(shù)可能不會(huì)被統(tǒng)計(jì)到。
主流的操作系統(tǒng)都會(huì)從內(nèi)核層進(jìn)行支持,所以這種方式對(duì)應(yīng)用的性能影響相對(duì)較少(具體多少和采樣頻率強(qiáng)相關(guān))。
在性能數(shù)據(jù)里,時(shí)間也是一個(gè)非常重要的指標(biāo),主要有兩類:
- CPU Time(CPU時(shí)間):
占用的CPU時(shí)間片的總和。
這個(gè)時(shí)間主要用來(lái)分析高CPU消耗。
- Wall Time(墻上時(shí)間):
真實(shí)流逝的時(shí)間。
除了CPU消耗,還有資源等待的時(shí)間等等,這個(gè)時(shí)間主要用來(lái)分析rt(響應(yīng)時(shí)間,Response time)。
性能數(shù)據(jù)獲取方式+時(shí)間指標(biāo)一共有四種組合方式,每一種都有它們的最適用的場(chǎng)景。
不過(guò)需要記住,Java應(yīng)用通常都需要至少5分鐘(這是一個(gè)經(jīng)驗(yàn)值,通常Server模式的JVM需要在方法執(zhí)行5000~10000次后才會(huì)進(jìn)行JIT編譯)的大流量持續(xù)測(cè)試才能使應(yīng)用的性能達(dá)到穩(wěn)定狀態(tài),所以除非你要分析的是應(yīng)用正在預(yù)熱時(shí)的性能,否則你需要等待5分鐘以上再開始收集性能數(shù)據(jù)。
Linux系統(tǒng)上面也有不少非常好用的性能監(jiān)控工具,如下圖:
2.1 構(gòu)造性能測(cè)試用例
通常我們都會(huì)分析一個(gè)或多個(gè)典型業(yè)務(wù)場(chǎng)景的性能,而不僅僅是某一個(gè)或多個(gè)API接口。
比如對(duì)于雙11大促,我們要分析的是導(dǎo)購(gòu)、交易、發(fā)優(yōu)惠券等業(yè)務(wù)場(chǎng)景的性能。
好的性能測(cè)試用例的需要能夠反映典型的用戶和系統(tǒng)行為(并不是所有的,我們無(wú)法做到100%反映真實(shí)用戶場(chǎng)景,只能逐漸接近它),比如一次下單平均購(gòu)買多少個(gè)商品(實(shí)際的用例里會(huì)細(xì)分為:購(gòu)買一個(gè)商品的占比多少,二個(gè)商品的占比多少,等等)、熱點(diǎn)售買商品的數(shù)量與成交占比、用戶數(shù)、商品數(shù)、熱點(diǎn)庫(kù)存分布、買家人均有多少?gòu)埲鹊取?/p>
像淘寶的雙11的壓測(cè)用例,像這樣關(guān)鍵的參數(shù)會(huì)多達(dá)200多個(gè)。
實(shí)際執(zhí)行時(shí),我們期望測(cè)試用例是可以穩(wěn)定持續(xù)的運(yùn)行的(比如不會(huì)跑30分鐘下單發(fā)現(xiàn)庫(kù)存沒了,優(yōu)惠券也沒了),緩存的命中率、DB流量等等的外部依賴也可以達(dá)到一個(gè)穩(wěn)定狀態(tài),在秒級(jí)時(shí)間(一般不需要更細(xì)了)粒度上應(yīng)用的性能也是穩(wěn)定的(即請(qǐng)求的計(jì)算復(fù)雜度在時(shí)間粒度上是均勻分布的,不會(huì)一會(huì)高一會(huì)低的來(lái)回抖動(dòng))。
為了配合性能測(cè)試用例的執(zhí)行,有的時(shí)候還需要應(yīng)用系統(tǒng)做一些相應(yīng)的改造。
比如對(duì)于會(huì)使用到緩存的場(chǎng)景來(lái)說(shuō),剛開始命中率肯定是不高的,但跑了一會(huì)兒過(guò)后就會(huì)慢慢的變?yōu)?00%,這顯然不是通常真實(shí)的情況,所以可能會(huì)配合寫一些邏輯,來(lái)讓緩存命中率一直維持在某個(gè)特定值上。
綜上,一套好的性能測(cè)試用例是開展后續(xù)工作所必不可少的,值得我們?cè)谒厦婊〞r(shí)間。
2.2 真實(shí)的測(cè)試環(huán)境
保持測(cè)試環(huán)境與其實(shí)環(huán)境的一致性是極其重要的,但是往往也是很難做到的,所以大多數(shù)互聯(lián)網(wǎng)公司的全鏈路壓測(cè)方案都是直接使用線上環(huán)境來(lái)做性能測(cè)試。
如果我們無(wú)法做到使用線上環(huán)境來(lái)做性能測(cè)試,那么就需要花上不少的精力來(lái)仔細(xì)對(duì)比我們所使用的環(huán)境與線上環(huán)境的差異,確保我們知道哪一些性能數(shù)據(jù)是可以值得相信的。
直接使用線上環(huán)境來(lái)做性能測(cè)試也并不是那么簡(jiǎn)單,這需要我們有一套整體解決方案來(lái)讓壓測(cè)流量與真實(shí)流量進(jìn)行區(qū)分,一般都是在流量中加一個(gè)壓測(cè)標(biāo)進(jìn)行全鏈路的透?jìng)鳌?/p>
同時(shí)基本上所有的基礎(chǔ)組件都需要進(jìn)行改造來(lái)支持壓測(cè),主要有:
- DB:
為業(yè)務(wù)表建立對(duì)應(yīng)的壓測(cè)表來(lái)存儲(chǔ)壓測(cè)數(shù)據(jù)。不使用增加字段做邏輯隔離的原因是容易把它們與正式數(shù)據(jù)搞混,同時(shí)也不便于單獨(dú)清理壓測(cè)數(shù)據(jù)。
不使用新建壓測(cè)庫(kù)的原因是:一方面它違背了我們使用線上環(huán)境做性能壓測(cè)的基本考慮,另一方面也會(huì)導(dǎo)致應(yīng)用端多了一倍的數(shù)據(jù)庫(kù)連接。
- 緩存:
為緩存Key增加特殊的前綴,如__yt_。
緩存大多數(shù)沒有表的概念,看起來(lái)就是一個(gè)巨大的Map存儲(chǔ)一樣,所以除了加固定前綴并沒有太好的辦法。
不過(guò)為了減少壓測(cè)數(shù)據(jù)的存儲(chǔ)成本,通常需要:1) 在緩存client包中做一些處理來(lái)減少壓測(cè)數(shù)據(jù)的緩存過(guò)期時(shí)間;2)緩存控制臺(tái)提供專門清理壓測(cè)數(shù)據(jù)的功能。
- 消息:
在發(fā)送、消費(fèi)時(shí)透?jìng)鲏簻y(cè)標(biāo)。
盡量做到不需要業(yè)務(wù)團(tuán)隊(duì)的開發(fā)同學(xué)感知,在消息的內(nèi)部結(jié)構(gòu)中增加是否是壓測(cè)數(shù)據(jù)的標(biāo)記,不需要業(yè)務(wù)團(tuán)隊(duì)申請(qǐng)新的壓測(cè)專用的Topic之類。
- RPC:
透?jìng)鲏簻y(cè)標(biāo)。
當(dāng)然,HTTP、DUBBO等具體的RPC接口透?jìng)鞯姆桨笗?huì)是不同的。
- 緩存、數(shù)據(jù)庫(kù) client包:
根據(jù)壓測(cè)標(biāo)做請(qǐng)求的路由。
這需要配合前面提到的具體緩存、DB的具體實(shí)現(xiàn)方案。
- 異步線程池:
透?jìng)鲏簻y(cè)標(biāo)。
為了減少支持壓測(cè)的改造代價(jià),通常都會(huì)使用ThreadLocal來(lái)存儲(chǔ)壓測(cè)標(biāo),所以當(dāng)使用到異步線程池的時(shí)候,需要記得帶上它。
- 應(yīng)用內(nèi)緩存:
做好壓測(cè)數(shù)據(jù)與正式數(shù)據(jù)的隔離。
如果壓測(cè)數(shù)據(jù)的主鍵或者其它的唯一標(biāo)識(shí)符可以讓我們顯著的讓它與正式數(shù)據(jù)區(qū)分開來(lái),也許不用做太多,否則我們也許需要考慮要么再new一套緩存、要么為壓測(cè)數(shù)據(jù)加上一個(gè)什么特別的前綴。
- 自建任務(wù):
透?jìng)鲏簻y(cè)標(biāo)。
需要我們自行做一些與前述提到的消息組件類似的事情,畢竟任務(wù)和消息從技術(shù)上來(lái)說(shuō)是很像很像的。
- 二方、三方接口:
具體分析與解決。
需要看二方、三方接口是否支持壓測(cè),如果支持那么很好,我們按照對(duì)方期望的方式進(jìn)行參數(shù)的傳遞即可,如果不支持,那么我們需要想一些別的奇技婬巧(比如開設(shè)一個(gè)壓測(cè)專用的商戶、賬戶之類)了。
為了降低性能測(cè)試對(duì)用戶的影響,通常都會(huì)選擇流量低峰時(shí)進(jìn)行,一般都是半夜。
當(dāng)然,如果我們能有一套相對(duì)獨(dú)立的折中方案,比如使用小得物環(huán)境、在支持單元化的系統(tǒng)中使用部分單元等等,就可以做到在任何時(shí)候進(jìn)行性能測(cè)試,實(shí)現(xiàn)性能測(cè)試的常態(tài)化。
2.3 JProfiler的使用
JProfiler是一款非常成熟的產(chǎn)品,很貴很好用,它是專門為Java應(yīng)用的性能分析所準(zhǔn)備的,而且是跨平臺(tái)的產(chǎn)品,是我經(jīng)常使用的工具。
它的大體的架構(gòu)如下圖所示,Linux agent加上Windows UI是最推薦的使用方式,它不但同時(shí)支持Instrumentation & Sampling,CPU Time & Wall Time的選項(xiàng),而且還擁有非常易用的圖形界面。
分析時(shí),我們只需要將其agent包上傳到應(yīng)用中的某個(gè)目錄中(如:/opt/jprofiler11.1.2),然后添加JVM的啟動(dòng)選項(xiàng)來(lái)加載它,我通常都這樣配置:
接下來(lái)我們重啟應(yīng)用,這里的修改就會(huì)生效了。使用這個(gè)配置,Java進(jìn)程在開始啟動(dòng)時(shí)需要等待JProfiler UI的連接才會(huì)繼續(xù)啟動(dòng),這樣我們可以進(jìn)行應(yīng)用啟動(dòng)時(shí)性能的分析了。
JProfiler的功能很多,就不一一介紹了,大家可以閱讀其官方文檔。
采集的性能數(shù)據(jù)還可以保存為*.jps文件,方便后續(xù)的分析與交流。
其典型的分析界面如下圖所示:
JProfiler的一些缺點(diǎn):
1)需要在Java應(yīng)用啟動(dòng)加載agent(當(dāng)然它也有啟動(dòng)后attach的方式,但是有不少的限制),不太便于短時(shí)間的分析一些緊急的性能問(wèn)題;
2)對(duì)Java應(yīng)用的性能影響偏大。使用采樣的方式來(lái)采集性能數(shù)據(jù)開銷肯定會(huì)低很多,但還是沒有接下來(lái)要介紹的perf做的更好。
2.4 perf的使用
perf是Linux上面當(dāng)之無(wú)愧的性能分析工具的一哥,這一點(diǎn)需要特別強(qiáng)調(diào)一下。
不但可以用來(lái)分析Linux用戶態(tài)應(yīng)用的性能,甚至還常用來(lái)分析內(nèi)核的性能。
它的模塊結(jié)構(gòu)如下圖所示:
想像一下這樣的場(chǎng)景,如果我們換了一家云廠商,或者云廠商的服務(wù)器硬件(主要就是CPU了)有了更新迭代,我們想知道具體性能變化的原因,有什么辦法嗎?
perf就能很好的勝任這個(gè)工作。
CPU的設(shè)計(jì)者為了幫助我們分析應(yīng)用執(zhí)行時(shí)的性能,專門設(shè)計(jì)了相關(guān)的硬件電路,PMU(性能監(jiān)控單元,Performance Monitor Unit)就是這其中最重要的部分。
簡(jiǎn)單來(lái)說(shuō)里面包含了很多性能計(jì)數(shù)器(圖中的PMCs,Performance Monitor Counters),perf可以讀取這些數(shù)據(jù)。
不僅如此,內(nèi)核層面還提供了很多軟件級(jí)別的計(jì)數(shù)器,perf同樣可以讀取它們。
一些和CPU架構(gòu)相關(guān)的關(guān)鍵指標(biāo),可以了解一下:
- IPC(每周期執(zhí)行指令條數(shù),Instruction per cycle):
基于功耗/性能的考慮,大多數(shù)服務(wù)器處理器的頻率都在2.5~2.8GHz的范圍,這代表同一時(shí)間片內(nèi)的周期數(shù)是差異不大的,所以單個(gè)周期能夠執(zhí)行的指令條數(shù)越多說(shuō)明我們的應(yīng)用優(yōu)化的越好。
過(guò)多的跳轉(zhuǎn)指令(即if else這類代碼)、浮點(diǎn)計(jì)算、內(nèi)存隨機(jī)訪問(wèn)等操作顯然是非常影響IPC的。
有的人比較喜歡說(shuō)CPI(Cycle per instruction),它是IPC的倒數(shù)。
- LLC Cache Miss(最后一級(jí)緩存丟失):
偏內(nèi)存型的應(yīng)用需要關(guān)注這個(gè)指標(biāo),過(guò)大的話代表我們沒有利用好處理器或操作系統(tǒng)的緩存預(yù)加載機(jī)制。
- Branch Misses(預(yù)測(cè)錯(cuò)誤的分支指令數(shù)):
這個(gè)值過(guò)高代表了我們的分支類代碼設(shè)計(jì)的不夠友好,應(yīng)該做一些調(diào)整盡量滿足處理器的分支預(yù)測(cè)算法的期望。
如果我們的分支邏輯依賴于數(shù)據(jù)的話,做一些數(shù)據(jù)的調(diào)整一樣可以提高性能(比如這個(gè)經(jīng)典案例:數(shù)據(jù)有100萬(wàn)個(gè)元素,值在0-255之間,需要統(tǒng)計(jì)值小于128的元素個(gè)數(shù)。
提前對(duì)數(shù)組排序再進(jìn)行for循環(huán)判斷會(huì)運(yùn)行的更快)。
因?yàn)閜erf是為L(zhǎng)inux上的原生應(yīng)用準(zhǔn)備的,所以直接使用它分析Java應(yīng)用程序的話,它只會(huì)把Java程序當(dāng)成一個(gè)普通的C++程序來(lái)看待,不能顯示出Java的調(diào)用棧和符號(hào)信息。
好消息是perf-map-agent插件項(xiàng)目解決了這個(gè)問(wèn)題,這個(gè)插件可以導(dǎo)出Java的符號(hào)信息并幫助perf進(jìn)行Java線程的棧回溯,這樣我們就可以使用perf來(lái)分析Java應(yīng)用程序的性能了。
執(zhí)行 perf top -p 后,就可以看到perf顯示的實(shí)時(shí)性能統(tǒng)計(jì)信息了,如下圖:
perf僅支持采樣 + CPU Time的工作模式,不過(guò)它的性能非常好,進(jìn)行普通的Java性能分析任務(wù)時(shí)通常只會(huì)引入5%以內(nèi)的額外開銷。
使用環(huán)境變量PERF_RECORD_FREQ來(lái)設(shè)置采樣頻率,推薦值是999。不過(guò)如你所見,它是標(biāo)準(zhǔn)的Linux命令行式的交互行為,不是那么方便。
同時(shí)雖然他是可以把性能數(shù)據(jù)錄制為文件供后續(xù)繼續(xù)分析的,但要記得同時(shí)保存Java進(jìn)程的符號(hào)文件,不然你就無(wú)法查看Java的調(diào)用棧信息了。
雖然限制不少,但是perf卻是最適合用來(lái)即時(shí)分析線上性能問(wèn)題的工具,不需要任何前期的準(zhǔn)備,隨時(shí)可用,同時(shí)對(duì)線上性能的影響也很小,可以很快的找到性能瓶頸點(diǎn)。
在安裝好perf(需要sudo權(quán)限)以及perf-map-agent插件后,通常使用如下的命令來(lái)打開它:
重點(diǎn)需要介紹的信息就是這么多,實(shí)踐過(guò)程中需要用好perf的話需要再查閱相關(guān)的一些文檔。
2.5 內(nèi)核態(tài)與用戶態(tài)
對(duì)操作系統(tǒng)有了解的同學(xué)會(huì)經(jīng)常聽到這兩個(gè)詞,也都知道經(jīng)常在內(nèi)核態(tài)與用戶態(tài)之間交互是非常影響性能的。
從執(zhí)行層面來(lái)說(shuō),它是處理器的設(shè)計(jì)者設(shè)計(jì)出來(lái)構(gòu)建如今穩(wěn)定的操作系統(tǒng)的基礎(chǔ)。
有了它,用戶態(tài)(x86上面是ring3)進(jìn)程無(wú)法執(zhí)行特權(quán)指令與訪問(wèn)內(nèi)核內(nèi)存。
大多數(shù)時(shí)候?yàn)榱税踩?,?nèi)核也不能把某部分內(nèi)核內(nèi)存直接映射到用戶態(tài)上,所以在進(jìn)行內(nèi)核調(diào)用時(shí),需要先將那部分參數(shù)寫入到特定的傳參位置,然后內(nèi)核再?gòu)倪@里把它想要的內(nèi)容復(fù)制走,所以多會(huì)一次內(nèi)存的復(fù)制開銷。
你看到了,內(nèi)核為了安全,總是小心翼翼的面對(duì)每一次的請(qǐng)求。
在Linux上,TCP協(xié)議支持是在內(nèi)核態(tài)實(shí)現(xiàn)的,曾經(jīng)這有很多充分的理由,但內(nèi)核上的更新迭代速度肯定是慢于如今互聯(lián)網(wǎng)行業(yè)的要求的,所以QUIC(Quick UDP Internet Connection,谷歌制定的一種基于UDP的低時(shí)延的互聯(lián)網(wǎng)傳輸層協(xié)議)誕生了。
如今主流的發(fā)展思路是能不用內(nèi)核就不用內(nèi)核,盡量都在用戶態(tài)實(shí)現(xiàn)一切。
有一個(gè)例外,就是搶占式的線程調(diào)度在用戶態(tài)做不到,因?yàn)閷?shí)現(xiàn)它需要的定時(shí)時(shí)鐘中斷只能在內(nèi)核態(tài)設(shè)置和處理。
協(xié)程技術(shù)一直是重IO型Java應(yīng)用減少內(nèi)核調(diào)度開銷的極好的技術(shù),但是很遺憾它需要執(zhí)行線程主動(dòng)讓出剩余時(shí)間片,不然與內(nèi)核線程關(guān)聯(lián)的多個(gè)用戶態(tài)線程就可能會(huì)餓死。
阿里巴巴的Dragonwell版JVM還嘗試了動(dòng)態(tài)調(diào)整策略(即用戶態(tài)線程不與固定的內(nèi)核態(tài)線程關(guān)聯(lián),在需要時(shí)可以切換),不過(guò)由于前述的時(shí)鐘中斷的限制,也不能工作的很好。
包括如今的虛擬化技術(shù),尤其是SR-IOV技術(shù),只需要內(nèi)核參與接口分配/回收的工作,中間的通信部分完全是在用戶態(tài)完成的,不需要內(nèi)核參與。
所以,如果你發(fā)現(xiàn)你的應(yīng)用程序在內(nèi)核態(tài)上耗費(fèi)了太多的時(shí)間,需要想一想是否可以讓它們?cè)谟脩魬B(tài)完成。
2.6 JVM關(guān)鍵指標(biāo)
JVM的指標(biāo)很多,但有幾個(gè)關(guān)鍵的指標(biāo)需要大家經(jīng)常關(guān)注。
1.GC次數(shù)與時(shí)間:包括Young GC、Full GC、Concurrent GC等等,Young GC頻率過(guò)高往往代表過(guò)多臨時(shí)對(duì)象的產(chǎn)生。
- Java堆大?。喊ㄕ麄€(gè)Java堆的大?。ㄓ蒟mx、Xms兩個(gè)參數(shù)控制),年輕代、老年代分別的大小。不同時(shí)指定Xms和Xmx很可能會(huì)讓你的Java進(jìn)程一直使用很小的堆空間,過(guò)大的老年代空間大多數(shù)時(shí)候也意味著內(nèi)存的浪費(fèi)(多分配一些給年輕代將顯著降低Young GC頻率)。
- 線程數(shù):通常我們采用的都是4C8G(4Core vCPU,8GB內(nèi)存)、8C16G的機(jī)型,分配出上千個(gè)線程大多數(shù)時(shí)候都是錯(cuò)誤的。
- Metaspace大小和使用率:不要讓JVM動(dòng)態(tài)的擴(kuò)展元空間的大小,盡量通過(guò)設(shè)置MetaspaceSize、MaxMetaspaceSize讓它固定住。我們需要知道我們的應(yīng)用到底需要多少元空間,過(guò)多的元空間占用以及過(guò)快的增長(zhǎng)都意味著我們可能錯(cuò)誤的使用了動(dòng)態(tài)代理或腳本語(yǔ)言。
- CodeCache大小和使用率:同樣的,不要讓JVM動(dòng)態(tài)的擴(kuò)展代碼緩存的大小,盡量通過(guò)設(shè)置InitialCodeCacheSize、ReservedCodeCacheSize讓它固定住。我們可以通過(guò)它的變化來(lái)發(fā)現(xiàn)最近是不是又引入了新的類庫(kù)。
- 堆外內(nèi)存大?。合拗谱畲蠖淹鈨?nèi)存的大小,計(jì)算好JVM各塊內(nèi)存的大小,不要給操作系統(tǒng)觸發(fā)OOM Killer的機(jī)會(huì)。
2.7 了解JIT
字節(jié)碼的解釋執(zhí)行肯定是相當(dāng)慢的,Java之所以這么流行和他擁有高性能的JIT(即時(shí),Just in time)編譯器也有很大的關(guān)系。
但編譯過(guò)程本身也是相當(dāng)消耗性能的,且由于Java的動(dòng)態(tài)特性,也很難做到像C/C++這樣的編程語(yǔ)言提前編譯為native code再執(zhí)行,這導(dǎo)致Java應(yīng)用的啟動(dòng)是相當(dāng)慢的(大多數(shù)都需要3分鐘以上,Windows操作系統(tǒng)的啟動(dòng)都不需要這么久),而且同一個(gè)應(yīng)用的多臺(tái)機(jī)器之間并不能共享JIT的經(jīng)驗(yàn)(這顯然是極大的浪費(fèi))。
我們使用的JVM都采用分層編譯的策略,根據(jù)優(yōu)化的程度不同從低到高分別是C1、C2、C3、C4,C4是最快的。
JIT編譯器會(huì)收集不少運(yùn)行時(shí)的數(shù)據(jù),來(lái)指導(dǎo)它的編譯策略,核心假設(shè)是可以逐步收集信息、僅編譯熱點(diǎn)方法和路徑。
但是這個(gè)假設(shè)并不總是對(duì)的,比如對(duì)于雙11大促的場(chǎng)景來(lái)說(shuō),我們的流量是到點(diǎn)突然垂直增加的,以及部分代碼分支在某個(gè)時(shí)間點(diǎn)之前并不會(huì)運(yùn)行(比如某種優(yōu)惠要零點(diǎn)過(guò)后才會(huì)生效可用)。
2.7.1 編譯
極熱的函數(shù)通常JIT編譯器會(huì)函數(shù)進(jìn)行內(nèi)聯(lián)(inlining)優(yōu)化,就相當(dāng)于直接把代碼抄寫到調(diào)用它的地方來(lái)減少一次函數(shù)調(diào)用的開銷,但是如果函數(shù)體過(guò)大的話(具體要看JVM的實(shí)現(xiàn),通常是幾百字節(jié))將不能內(nèi)聯(lián),這也是為什么編程規(guī)范里面通常都會(huì)說(shuō)不要將一個(gè)函數(shù)寫的過(guò)大的原因。
JVM并不會(huì)對(duì)所有執(zhí)行過(guò)的方法都進(jìn)行JIT優(yōu)化,通常需要5000~10000次的執(zhí)行后才進(jìn)行,而且它還僅僅編譯那些曾經(jīng)執(zhí)行過(guò)的分支(以減少編譯所需要的時(shí)間和Code Cache的占用,優(yōu)化CPU的執(zhí)行性能)。
所以在寫代碼的時(shí)候,if代碼后面緊跟的代碼塊最好是較大概率會(huì)執(zhí)行到的,同時(shí)盡量讓代碼執(zhí)行流比較固定。
阿里巴巴的Dragonwell版JVM新增了一些功能,可以讓JVM在運(yùn)行時(shí)記錄編譯了哪些方法,再把它們寫入文件中(還可以分發(fā)給應(yīng)用集群中別的機(jī)器),下次JVM啟動(dòng)時(shí)可以利用這部分信息,在第一次運(yùn)行這些方法時(shí)就觸發(fā)JIT編譯,而不是在執(zhí)行上千次以后,這會(huì)極大的提升應(yīng)用啟動(dòng)的速度以及啟動(dòng)時(shí)CPU的消耗。
不過(guò)動(dòng)態(tài)AOP(Aspect Oriented Programming)代碼以及l(fā)ambda代碼將不能享受這個(gè)紅利,因?yàn)樗鼈冞\(yùn)行時(shí)實(shí)際生成的函數(shù)名都是形如MethodAccessor$1586 這類以數(shù)字結(jié)尾的不穩(wěn)定的名稱,這次是1586,下一次就不知道什么了。
2.7.2 退優(yōu)化
JIT編譯器的激進(jìn)優(yōu)化并不總是對(duì)的,如果它發(fā)現(xiàn)目前需要的執(zhí)行流在以前的編譯中被省略了的話,它就會(huì)進(jìn)行退優(yōu)化,即重新提交該方法的編譯請(qǐng)求。
在新的編譯請(qǐng)求完成之前,該方法很大可能是進(jìn)行解釋執(zhí)行(如果存在還未丟棄的低階編譯代碼,比如C1,那么就會(huì)執(zhí)行C1的代碼),加上編譯線程的開銷,這會(huì)導(dǎo)致短時(shí)間內(nèi)應(yīng)用性能的下降。
在雙11大促這種場(chǎng)景下,也就是零點(diǎn)的高峰時(shí)刻,由于退優(yōu)化的發(fā)生,導(dǎo)致應(yīng)用的性能比壓測(cè)時(shí)有相當(dāng)顯著的降低。
阿里巴巴的Dragonwell版JVM在這一塊也提供了一些選項(xiàng),可以在JIT編譯時(shí)去除一些激進(jìn)優(yōu)化,以防止退優(yōu)化的發(fā)生。
當(dāng)然,這會(huì)導(dǎo)致應(yīng)用的性能有微弱的下降。
2.8 真實(shí)案例
在性能優(yōu)化的實(shí)踐過(guò)程中,有一句話需要反復(fù)深刻的理解:通過(guò)數(shù)據(jù)反映一切,而不是聽說(shuō)或者經(jīng)驗(yàn)。
下面列舉一個(gè)在重構(gòu)項(xiàng)目中進(jìn)行優(yōu)化的應(yīng)用的性能比較數(shù)據(jù),以展示如何利用我們前面說(shuō)到的知識(shí)。
這個(gè)應(yīng)用是偏末端的應(yīng)用,下游基本不再依賴其它應(yīng)用。
- 特別說(shuō)明,【此案例非得物案例】,也不對(duì)應(yīng)任何一個(gè)真實(shí)的案例。
應(yīng)用容器:8C32G,Intel 8269CY處理器,8個(gè)處理器綁定到4個(gè)物理核的8個(gè)HT上。
老應(yīng)用:12.177.126.52,12.177.126.141
新應(yīng)用:12.177.128.150, 12.177.128.28
測(cè)試接口與流量:
時(shí)間:2021-05-03 基礎(chǔ)數(shù)據(jù):
緩存訪問(wèn):
DB訪問(wèn):
2.8.1 初步發(fā)現(xiàn)
通過(guò)外部依賴的差異可以發(fā)現(xiàn),新老應(yīng)用的代碼邏輯會(huì)有不同,需要繼續(xù)深入評(píng)估差異是什么。通常在做收集性能數(shù)據(jù)的同時(shí),我們需要有一個(gè)簡(jiǎn)單的分析和判斷,首先確保業(yè)務(wù)邏輯的正確性,不然性能數(shù)據(jù)就沒有多少意義。
Java Exception是很消耗性能的,主要消耗在收集異常棧信息,新老應(yīng)用較大的異常數(shù)區(qū)別需要找到原因并解決。
新應(yīng)用的接口“下單確認(rèn)優(yōu)惠”RT有過(guò)于明顯的下降,其它接口都是提升的,說(shuō)明很可能存在執(zhí)行路徑上較大的差別,也需要深入的分析。
三 . 開始做性能優(yōu)化
和獲取性能數(shù)據(jù)需要從底層逐步了解到上層業(yè)務(wù)不同,做性能優(yōu)化卻是從上層業(yè)務(wù)開始,逐步推進(jìn)到底層,即從高到低進(jìn)行分層優(yōu)化。
越高層的優(yōu)化往往難度更低,而且收益還越大,只是需要與業(yè)務(wù)的深度結(jié)合。
越低層的優(yōu)化往往難度比較大,很難獲得較大的收益(畢竟一堆技術(shù)精英一直在做著呢),但是通用性比較好,往往可以適用于多類業(yè)務(wù)場(chǎng)景。
接下來(lái)分別聊一聊每一層可以思考的一些方向和實(shí)際的例子。
3.1 優(yōu)化的目的與原則
在聊具體的優(yōu)化措施之前,我們先聊一聊為什么要做性能優(yōu)化。
多數(shù)情況下,性能優(yōu)化的目的都是為了成本、效率與穩(wěn)定。
達(dá)到同樣的業(yè)務(wù)效果,使用更少的資源,或者帶來(lái)更好的用戶體驗(yàn)(通常是指頁(yè)面的響應(yīng)更快)。
不怎么考慮成本的技術(shù)方案往往沒有太多的挑戰(zhàn),對(duì)于電商平臺(tái)來(lái)說(shuō),我們常常用單訂單成本來(lái)衡量機(jī)器成本,比如淘寶這個(gè)值可能在0.17元左右。
業(yè)務(wù)發(fā)展的早期往往并不是那么在意成本,反而更加看重效率,等到逐步成熟起來(lái)過(guò)后,會(huì)慢慢的開始重視成本,通俗的講就是開始比的是有沒有,然后比的是好不好。
所以在不同的時(shí)期,我們進(jìn)行性能優(yōu)化的目的和方向會(huì)有所側(cè)重。
互聯(lián)網(wǎng)行業(yè)是一個(gè)快速發(fā)展的行業(yè),研發(fā)效率對(duì)業(yè)務(wù)的健康發(fā)展是至關(guān)重要的,在進(jìn)行優(yōu)化的過(guò)程中,我們?cè)诩夹g(shù)方案的選擇上需要兼顧研發(fā)效率的提升(至少不能損害過(guò)多),給人一種“它本來(lái)應(yīng)該就是這樣”的感覺,而不是做一些明顯無(wú)法長(zhǎng)期持續(xù)、后期維護(hù)成本過(guò)高的設(shè)計(jì)。
好的優(yōu)化方案就像藝術(shù)品一樣,每一個(gè)看到的人都會(huì)為之贊嘆。
3.2 業(yè)務(wù)
大家為什么會(huì)在雙11的零點(diǎn)開始上各大電商網(wǎng)站買東西?
春節(jié)前大家為什么都在上午10點(diǎn)搶火車票?
等等,其實(shí)都是業(yè)務(wù)上的設(shè)計(jì)。
準(zhǔn)備一大波機(jī)器資源使用2個(gè)月就為了雙11峰值的那幾分鐘,實(shí)際上是極大的成本浪費(fèi),所以為了不那么浪費(fèi),淘寶的雙11預(yù)售付尾款的時(shí)間通常都放在凌晨1點(diǎn)。
12306早幾年是每臨進(jìn)春節(jié)必掛,因?yàn)橄胍丶业挠巫訉?shí)在是太多,所以后面慢慢按照車次將售賣時(shí)間打散,參考其公告:
自今年1月8日起,為避免大量旅客在互聯(lián)網(wǎng)排隊(duì)購(gòu)票,把原來(lái)的8點(diǎn)、10點(diǎn)、12點(diǎn)、15點(diǎn)四個(gè)時(shí)間節(jié)點(diǎn)放票改為15個(gè)節(jié)點(diǎn)放票,即:8點(diǎn)-18點(diǎn),其間每小時(shí)或每半小時(shí)均有部分新票起售。
這些策略都可以極大的降低系統(tǒng)的峰值流量,同時(shí)對(duì)于用戶使用體驗(yàn)來(lái)說(shuō)基本是無(wú)感的,諸如此類的許多優(yōu)化是我們最開始就要去思考的(不過(guò)請(qǐng)記住永遠(yuǎn)要把業(yè)務(wù)效果放在第一位,和業(yè)務(wù)講業(yè)務(wù),而不是和業(yè)務(wù)講技術(shù))。
3.3 系統(tǒng)架構(gòu)
諸如商品詳情頁(yè)動(dòng)靜分離(靜態(tài)頁(yè)面與動(dòng)態(tài)頁(yè)面分開不同系統(tǒng)訪問(wèn)),用戶接口層(即HTTP/S層)與后端(Java層)合并部署等等,都是架構(gòu)優(yōu)化的成功典范。
業(yè)務(wù)架構(gòu)師往往會(huì)將系統(tǒng)設(shè)計(jì)為很多層,但是在運(yùn)行時(shí),他們往往可以部署在一塊兒,以減少跨進(jìn)程、跨機(jī)器、跨地域通信。
淘寶的單元化架構(gòu)在性能上來(lái)看也是一個(gè)很好的設(shè)計(jì),一個(gè)交易請(qǐng)求幾乎所有的處理都可以封閉在單元內(nèi)完成,減少了很多跨地域的網(wǎng)絡(luò)長(zhǎng)傳帶寬需求。
富客戶端方案對(duì)于像商品信息、用戶信息等基礎(chǔ)數(shù)據(jù)來(lái)說(shuō)也是很好的方案,畢竟大多數(shù)情況下它們都是訪問(wèn)Redis等緩存,多一次到服務(wù)端的RPC請(qǐng)求總是顯得很多余,當(dāng)然,后續(xù)需要升級(jí)數(shù)據(jù)結(jié)構(gòu)的時(shí)候則需要做更多的工作。
關(guān)于架構(gòu)的討論是永恒的話題,同時(shí)不同的公司有不同的背景,實(shí)際進(jìn)行優(yōu)化時(shí)也需要根據(jù)實(shí)際情況來(lái)取舍。
3.4 調(diào)用鏈路
在分布式系統(tǒng)里不可避免需要依賴很多下游服務(wù)才能完成業(yè)務(wù)動(dòng)作,怎么依賴、依賴什么接口、依賴多少次則是需要深入思考的問(wèn)題。
借助調(diào)用鏈查看工具(在得物,這個(gè)工具應(yīng)該是dependency),我們可以仔細(xì)分析每一個(gè)業(yè)務(wù)請(qǐng)求,然后去思考它是不是最優(yōu)的方式。
舉一個(gè)我聽說(shuō)過(guò)的例子(特別說(shuō)明,【此案例非得物案例】):
背景:營(yíng)銷團(tuán)隊(duì)接到了一個(gè)拉新的需求,它會(huì)在公司周年慶的10:00形成爆點(diǎn),預(yù)計(jì)會(huì)產(chǎn)生最高30萬(wàn)的UV,然后只需要點(diǎn)擊活動(dòng)頁(yè)的參與按鈕(預(yù)估轉(zhuǎn)化率是75%),就會(huì)彈出一個(gè)組團(tuán)頁(yè),讓用戶邀請(qǐng)他的好友參與組團(tuán),每多邀請(qǐng)一個(gè)朋友,在團(tuán)內(nèi)的用戶都可以享受更多的優(yōu)惠折扣。
營(yíng)銷團(tuán)隊(duì)為組團(tuán)頁(yè)提供了一個(gè)新的后臺(tái)接口,最開始這個(gè)接口需要完成這些事:
在“為用戶創(chuàng)建一個(gè)新團(tuán)”這一步,會(huì)同時(shí)將新團(tuán)的信息持久化到數(shù)據(jù)庫(kù)中,按照業(yè)務(wù)的轉(zhuǎn)化率預(yù)估,這會(huì)有30W*75%=22.5W QPS的峰值流量,基本上我們需要10個(gè)左右的數(shù)據(jù)庫(kù)實(shí)例才能支撐這么高的并發(fā)寫入。
但這是必要的嗎?
顯然不是,我們都知道這類需要用戶轉(zhuǎn)發(fā)的活動(dòng)的轉(zhuǎn)化率是有多么的低(大多數(shù)不到8%),同時(shí)對(duì)于那些沒人參與的團(tuán),將它們的信息保存在數(shù)據(jù)庫(kù)中也是意義不大的。
最后的優(yōu)化方案是:
1)在“為用戶創(chuàng)建一個(gè)新團(tuán)”時(shí),僅將團(tuán)信息寫入Redis緩存中;
2)在用戶邀請(qǐng)的朋友同意參團(tuán)時(shí),再將團(tuán)信息持久化到數(shù)據(jù)庫(kù)中。新的設(shè)計(jì),僅僅需要1.8W QPS的數(shù)據(jù)庫(kù)并發(fā)寫入量,使用原有的單個(gè)數(shù)據(jù)庫(kù)實(shí)例就可以支撐,在業(yè)務(wù)效果上也沒有任何區(qū)別。
除了上述的例子,鏈路優(yōu)化還有非常多的方法,比如多次調(diào)用的合并、僅在必要時(shí)才調(diào)用(類似COW [Copy on write]思想)等等,需要大家結(jié)合具體的場(chǎng)景去分析設(shè)計(jì)。
3.5 應(yīng)用代碼
應(yīng)用代碼的優(yōu)化往往是我們最熱衷和擅長(zhǎng)的,畢竟業(yè)務(wù)與系統(tǒng)架構(gòu)的優(yōu)化往往需要架構(gòu)師出馬。
JProfiler或者perf的剖析(Profiling)數(shù)據(jù)是非常有用的參考,任何不基于實(shí)際運(yùn)行數(shù)據(jù)的猜測(cè)往往會(huì)讓我們誤入歧途,接下來(lái)我們需要做的大多數(shù)時(shí)候都是“找熱點(diǎn) -> 優(yōu)化” ,然后“找熱點(diǎn) -> 優(yōu)化”,然后一直循環(huán)。
找熱點(diǎn)不是那么難,難在準(zhǔn)確的分析代碼的邏輯然后判斷到底它應(yīng)該消耗多少資源(通常都是CPU),然后制定優(yōu)化方案來(lái)達(dá)到目標(biāo),這需要相當(dāng)多的優(yōu)化經(jīng)驗(yàn)。
從我做過(guò)的性能優(yōu)化來(lái)總結(jié),大概主要的問(wèn)題都發(fā)生在這些地方:
- 和字符串過(guò)不去:非常多的代碼喜歡將多個(gè)Java變量使用StringBuilder拼接起來(lái)(那些連StringBuilder都不會(huì)用,只會(huì)使用 + 的家伙就更讓人頭疼了),然后再找準(zhǔn)時(shí)機(jī)spilt成多個(gè)String,然后再轉(zhuǎn)換成別的類型(Long等)。好吧,下次使用StringBuilder時(shí)記得指定初始的容量大小。
- 日志滿天飛:管它有用沒有,反正打了是不會(huì)錯(cuò)的,畢竟誰(shuí)都經(jīng)歷過(guò)沒有日志時(shí)排查問(wèn)題的痛苦。怎么說(shuō)呢,打印有用的、有效的日志是程序員的必修課。
- 喜愛Exception:不知道是不是某些Java的追隨者吹過(guò)頭了,說(shuō)什么Java的Exception和C/C++的錯(cuò)誤碼一樣高效。然而事實(shí)并不是這樣的,Exception進(jìn)行調(diào)用棧的回溯是相當(dāng)消耗性能的,尤其是還需要將它們打印在日志中的時(shí)候,會(huì)更加糟糕。
- 容器的深拷貝:List、HashMap等是大家非常喜歡的Java容器,但Java語(yǔ)言并沒有好的機(jī)制阻止別人修改它,所以大家常常深拷貝一個(gè)新的出來(lái),反正也就是一句代碼的事兒。
- 對(duì)JSON情有獨(dú)鐘:將對(duì)象序列化為JSON string,將JSON string反序列化為對(duì)象。前者主要用來(lái)打日志,后者主要用來(lái)讀配置。JSON是挺好,只是請(qǐng)別用的到處都是(還把每個(gè)屬性的名字都取的老長(zhǎng))。
- 重復(fù)重復(fù)再重復(fù):一個(gè)請(qǐng)求里查詢同樣的商品3次,查詢同樣的用戶2次,查詢同樣的緩存5次,都是常有的事,也許多查詢幾次一致性更好吧 :(。還有一些基本不會(huì)變的配置,也會(huì)放到緩存中,每次使用的時(shí)候都會(huì)從緩存中讀出來(lái),反序列化,然后再使用,嗯,挺重的。
- 多線程的樂趣:不會(huì)寫多線程程序的開發(fā)不是好開發(fā),所以大家都喜歡new線程池,然后異步套異步。在流量很低的時(shí)候,看起來(lái)多線程的確解決了問(wèn)題(比如RT的確變小了),但是流量上來(lái)過(guò)后,問(wèn)題反而惡化了(畢竟我們主流的機(jī)器都是8核的)。
再重申一遍,在這一步,找到問(wèn)題并不是太困難,找到好的優(yōu)化方案卻是很困難和充滿考驗(yàn)的。
3.6 緩存
大多數(shù)的緩存都是Key、Value的結(jié)構(gòu),選擇緊湊的Key、Value以及高效的序列化、反序列化算法是重中之重(二進(jìn)制序列化協(xié)議比文本序列化協(xié)議快的太多了)。
還有的緩存是Prefix、Key、Value的結(jié)構(gòu),主要的區(qū)別是誰(shuí)來(lái)決定實(shí)際的數(shù)據(jù)路由到哪臺(tái)服務(wù)器進(jìn)行處理。單條緩存不能太大,基本上大于64KB就需要小心了,因?yàn)樗偸怯赡骋慌_(tái)實(shí)際的服務(wù)器在處理,很容易將出口寬帶或計(jì)算性能打滿。
3.7 DB
數(shù)據(jù)庫(kù)比起緩存來(lái)說(shuō)他能抗的流量就低太多了,基本上是差一個(gè)數(shù)量級(jí)。
SQL通信協(xié)議雖然很易用,但實(shí)際上是非常低效的通信協(xié)議。關(guān)于DB的優(yōu)化,通常都是從減少寫入量、減少讀取量、減少交互次數(shù)、進(jìn)行批處理等等方面著手。
DB的優(yōu)化是一門復(fù)雜的學(xué)問(wèn),很難用一篇文章說(shuō)清楚,這里僅舉一些我認(rèn)為比較有代表性的例子:
- 使用MultiQuery減少網(wǎng)絡(luò)交互:MySQL等數(shù)據(jù)庫(kù)都支持將多條SQL語(yǔ)言寫到一起,一起發(fā)送給DB服務(wù)器,這會(huì)將多次網(wǎng)絡(luò)交互減少為一次。
- 使用BatchInsert代替多次insert:這個(gè)很常見。
- 使用KV協(xié)議取代SQL:阿里云數(shù)據(jù)庫(kù)團(tuán)隊(duì)在數(shù)據(jù)庫(kù)服務(wù)器上面外掛了一個(gè)KV引擎,可以直接讀取InnoDB引擎中的數(shù)據(jù),bypass掉了數(shù)據(jù)庫(kù)的數(shù)據(jù)層,使得基于唯一鍵的查詢可以比使用SQL快10倍。
- 與業(yè)務(wù)結(jié)合:淘寶下單時(shí)可以同時(shí)使用多達(dá)10個(gè)紅包,這意味著一次下單需要發(fā)送至多10次update SQL。假設(shè)一次下單使用了N個(gè)紅包,基于對(duì)業(yè)務(wù)行為的分析,會(huì)發(fā)現(xiàn)前N-1個(gè)都是全額使用的,最后一個(gè)可能會(huì)部分使用。對(duì)于使用完的紅包,我們可以使用一條SQL就完成更新。
- 熱點(diǎn)優(yōu)化:庫(kù)存的熱點(diǎn)問(wèn)題是每個(gè)電商平臺(tái)都面臨的問(wèn)題,使用數(shù)據(jù)庫(kù)來(lái)扣減庫(kù)存肯定是可靠性最高的方案,但是基本上都很難突破500tps的瓶頸。阿里云數(shù)據(jù)庫(kù)團(tuán)隊(duì)設(shè)計(jì)了新的SQL hint,配合上第1條說(shuō)的MultiQuery技術(shù),與數(shù)據(jù)庫(kù)進(jìn)行一次交互就可以完成庫(kù)存的扣減。同時(shí)加上數(shù)據(jù)庫(kù)內(nèi)核的針對(duì)性優(yōu)化,已經(jīng)可以實(shí)現(xiàn)8W tps的熱點(diǎn)扣減能力。下表中的commit_on_success用來(lái)表明,如果update執(zhí)行成功就立即提交,這樣可以讓庫(kù)存熱點(diǎn)行的鎖占用時(shí)間降到最低。target_affect_row(1)以及rollback_on_fail用來(lái)限制當(dāng)庫(kù)存售罄時(shí)(即inv_count - 1 >= 0不成立)update執(zhí)行失敗并回滾整個(gè)事務(wù)(即前面插入的庫(kù)存流水作廢)。
3.8 運(yùn)行環(huán)境
我們的代碼是運(yùn)行在某個(gè)環(huán)境中的,這個(gè)環(huán)境有很多知識(shí)是我們需要了解的,如果上面所有的優(yōu)化完成后還不能滿足要求,那么我們也不得不向下深入。
這可能是一個(gè)困難的過(guò)程,但也會(huì)是一個(gè)有趣的過(guò)程,因?yàn)槟憬K于有了和各領(lǐng)域的大佬們交流討論的機(jī)會(huì)。
3.8.1 中間件
目前大多數(shù)中間件的代碼是和我們的業(yè)務(wù)代碼運(yùn)行在一起的,比如監(jiān)控采集、消息client、RPC client、配置推送 client、DB連接組件等等。如果你發(fā)現(xiàn)這些組件的性能問(wèn)題,那么可以大膽的提出來(lái),不要害怕傷害到誰(shuí)。
我遇到過(guò)這樣的一些場(chǎng)景:
- 應(yīng)用偶爾會(huì)大量的發(fā)生ygc:
排查到的原因是,在我們依賴的服務(wù)發(fā)生地址列表變化(比如發(fā)生了重啟、掉線、擴(kuò)容等場(chǎng)景)時(shí),RPC client會(huì)接收到大量的推送,然后解析這些推送的信息,然后再更新一大堆內(nèi)存結(jié)構(gòu)。
提出的優(yōu)化建議是:
1)地址推送從全量推送改變?yōu)樵隽客扑停?
2)地址列表從掛接到服務(wù)接口維度更改為掛接到應(yīng)用維度。
- DB連接組件過(guò)多的字符串拼接:
DB連接組件需要進(jìn)行SQL的解析來(lái)計(jì)算分庫(kù)分表等信息,但實(shí)現(xiàn)上面不夠優(yōu)雅,拼接的字符串過(guò)多了,導(dǎo)致執(zhí)行SQL時(shí)內(nèi)存消耗過(guò)多。
3.8.2 容器
關(guān)于容器技術(shù)本身大多數(shù)時(shí)候我們做不了什么,往往就是盡量采用最新的技術(shù)(比如使用阿里云的神龍服務(wù)器什么的),不過(guò)在梆核(即容器調(diào)度)方面往往可以做不少事。
我們的應(yīng)用和誰(shuí)運(yùn)行在一起、相互之間有資源爭(zhēng)搶嗎、有沒有跨NUMA調(diào)度、有沒有資源超賣等等問(wèn)題需要我們關(guān)注(當(dāng)然,這需要容器團(tuán)隊(duì)提供相應(yīng)的查看工具)。
這里主要有兩個(gè)需要考慮的點(diǎn):
- 是不是支持離在線混部:
在線任務(wù)要求實(shí)時(shí)響應(yīng),而離線任務(wù)的運(yùn)行又需要耗費(fèi)非常多的機(jī)器。在雙11大促這樣的場(chǎng)景,把離線機(jī)器借過(guò)來(lái)用幾個(gè)小時(shí)就可以減少相應(yīng)的在線機(jī)器采購(gòu),能省下很多錢。
- 基于業(yè)務(wù)的調(diào)度:
把高消耗的應(yīng)用和低消耗的應(yīng)用部署在一起,同時(shí)如果雙方的峰值時(shí)刻還不完全相同,那就太美妙啦。
3.8.3 JVM
為了解決重IO型應(yīng)用線程過(guò)多的問(wèn)題開發(fā)了協(xié)程。
為了解決Java容器過(guò)多小對(duì)象的問(wèn)題(如HashMap的K, V都只能是包裝類型)開發(fā)了值容器。
為了解決Java堆過(guò)大時(shí)GC時(shí)間過(guò)長(zhǎng)的問(wèn)題(當(dāng)然還有覺得Java的內(nèi)存管理不夠靈活的原因)開發(fā)了GCIH(GC Invisible Heap,淘寶雙11期間部分熱點(diǎn)優(yōu)惠活動(dòng)的數(shù)據(jù)都是存在GCIH當(dāng)中的)。
為了解決Java啟動(dòng)時(shí)的性能問(wèn)題(即代碼要跑好幾千次才進(jìn)行JIT,而且每次啟動(dòng)都還要重復(fù)這個(gè)過(guò)程)開發(fā)了啟動(dòng)Hint功能。
為了解決業(yè)務(wù)峰值時(shí)刻JIT退優(yōu)化的問(wèn)題(即平時(shí)不使用的代碼執(zhí)行路徑在業(yè)務(wù)峰值時(shí)候需要使用,比如0點(diǎn)才生效的優(yōu)惠)開發(fā)了JIT編譯激進(jìn)優(yōu)化去除選項(xiàng)。
雖然目前JVM的實(shí)現(xiàn)就是你知道的那樣,但是并不代表這樣做就一直是合理的。
3.8.4 操作系統(tǒng)
基本上我們都是使用Linux操作系統(tǒng),新版本的內(nèi)核通常會(huì)帶來(lái)一些新功能和性能的提升,同時(shí)操作系統(tǒng)還需要為支撐容器(即Docker等)做不少事情。
對(duì)Host操作系統(tǒng)來(lái)說(shuō),開啟透明大頁(yè)、配置好網(wǎng)卡中斷CPU打散、配置好NUMA、配置好NTP(Network Time Protocol)服務(wù)、配置好時(shí)鐘源(不然clock_gettime可能會(huì)很慢)等等都是必要的。
還有就是需要做好各種資源的隔離,比如CPU隔離(高優(yōu)先級(jí)任務(wù)優(yōu)先調(diào)度、LLC隔離、超線程技術(shù)隔離等)、內(nèi)存隔離(內(nèi)存寬帶、內(nèi)存回收隔離避免全局內(nèi)存回收)、網(wǎng)絡(luò)隔離(網(wǎng)絡(luò)寬帶、數(shù)據(jù)包金銀銅等級(jí)劃分)、文件IO隔離(文件IO寬帶的上限與下限、特定文件操作限制)等等。
大多數(shù)內(nèi)核級(jí)別的優(yōu)化都不是我們能做的,但我們需要知道關(guān)鍵的一些影響性能的內(nèi)核參數(shù),并能夠理解大多數(shù)內(nèi)核機(jī)制的工作原理。
3.9 硬件
通常我們都是使用Intel的x86架構(gòu)的CPU,比如我們正在使用的Intel 8269CY,不過(guò)它的單顆售價(jià)得賣到4萬(wàn)多塊人民幣,卻只有區(qū)區(qū)26C52T(26核52線程)。
相比之下,AMD的EPYC 7763的規(guī)格就比較牛逼了(64C128T,256MB三級(jí)緩存,8通道 DDR4 3200MHz內(nèi)存,擁有204GB/s的超高內(nèi)存寬帶),但卻只要3萬(wàn)多一顆。
當(dāng)然,用AMD 2021年的產(chǎn)品和Intel 2019的產(chǎn)品對(duì)比并不是太公平,主要Intel 2021的新品Intel Xeon Platinum 8368Q處理器并不爭(zhēng)氣,僅僅只是提升到了38C76T而已(雖然和自家的上一代產(chǎn)品相比已經(jīng)大幅提升了近50%)。
除了x86處理器,ARM 64位處理器也在向服務(wù)端產(chǎn)品發(fā)力,而且這個(gè)產(chǎn)業(yè)鏈還可以實(shí)現(xiàn)全國(guó)產(chǎn)化。
華為2019年初發(fā)布的鯤鵬920-6426處理器,采用7nm工藝,具備64個(gè)CPU核,主頻2.6GHz。
雖然單核性能上其只有Intel 8269CY的近2/3,但是其CPU核數(shù)卻要多上一倍還多,加上其售價(jià)親民,同樣計(jì)算能力的情況下CPU部分的成本會(huì)下降近一半(當(dāng)然計(jì)算整個(gè)物理機(jī)成本的話其實(shí)下降有限)。
2020年雙11開始,淘寶在江蘇南通部署了支撐1萬(wàn)筆/s交易的國(guó)產(chǎn)化機(jī)房,正是采用了鯤鵬920-6426處理器,同時(shí)在2021年雙11,更是用上了阿里云自主研發(fā)的倚天710處理器(也是采用ARM 64位架構(gòu))。
在未來(lái),更是有可能基于RISC-V架構(gòu)設(shè)計(jì)自己的處理器。這些事實(shí)都在說(shuō)明,在處理器的選擇上,我們還是有不少空間的。
除了采用通用處理器,在一些特殊的計(jì)算領(lǐng)域,我們還可以采用專用的芯片,比如:使用GPU加速深度學(xué)習(xí)計(jì)算,在AI推理時(shí)使用神經(jīng)網(wǎng)絡(luò)加速芯片-含光NPU,以及使用FPGA芯片進(jìn)行高性能的網(wǎng)絡(luò)數(shù)據(jù)處理(阿里云神龍服務(wù)器上使用的神龍MOC卡)等等。
曾經(jīng)還有人想過(guò)設(shè)計(jì)可以直接運(yùn)行Java字節(jié)碼的處理器,雖然最終因?yàn)閺?fù)雜度太高而放棄。
這一切都說(shuō)明,硬件也是一直在根據(jù)使用場(chǎng)景在不斷的進(jìn)化之中的,永遠(yuǎn)要充滿想像。