您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“C/C++的性能如何優(yōu)化”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“C/C++的性能如何優(yōu)化”吧!
先敬上代碼:
#include <stdlib.h> #define CACHE_LINE __attribute__((aligned(64))) struct S1 { int r1; int r2; int r3; S1 ():r1 (1), r2 (2), r3 (3){} } CACHE_LINE; void add(const S1 smember[],int members,long &total) { int idx = members; do { total += smember[idx].r1; total += smember[idx].r2; total += smember[idx].r3; }while(--idx); } int main (int argc, char *argv[]) { const int SIZE = 204800; S1 *smember = (S1 *) malloc (sizeof (S1) * SIZE); long total = 0L; int loop = 10000; while (--loop) { // 方便對(duì)比測(cè)試 add(smember,SIZE,total); } return 0; }
注:代碼邏輯比較簡(jiǎn)單就是做一個(gè)累加操作,僅僅是為了演示。
編譯+運(yùn)行:
g++ cache_line.cpp -o cache_line ; task_set -c 1 ./cache_line
下圖是示例cache_line在CPU 1核心上運(yùn)行,CPU利用率達(dá)到99.7%,此時(shí)CPU基本上是滿載的,那么我們?nèi)绾沃肋@個(gè)cpu運(yùn)行cache_line 服務(wù)過(guò)程中是否做的都是有用功,是否還有優(yōu)化空間?
有的同學(xué)可能說(shuō),可以用perf 進(jìn)行分析尋找熱點(diǎn)函數(shù)。確實(shí)是可以使用perf,但是perf只能知道某個(gè)函數(shù)是熱點(diǎn)(或者是某些匯編指令),但是沒(méi)法確認(rèn)引起熱點(diǎn)的是CPU中的哪些操作存在瓶頸,比如取指令、解碼、.....
如果你還在為判斷是CPU哪些操作導(dǎo)致服務(wù)性能瓶頸而不知所措,那么這篇文章將會(huì)你給你授道解惑。本文主要通過(guò)介紹自頂向下分析方法(TMAM)方法論來(lái)快速、精準(zhǔn)定位CPU性能瓶頸以及相關(guān)的優(yōu)化建議,幫助大家提升服務(wù)性能。為了讓大家更好的理解本文介紹的方法,需要準(zhǔn)備些知識(shí)。
現(xiàn)代的計(jì)算機(jī)一般都是馮諾依曼計(jì)算機(jī)模型都有5個(gè)核心的組件:運(yùn)算、存儲(chǔ)、控制、輸入、輸出。本文介紹的方法與CPU有關(guān),CPU執(zhí)行過(guò)程中涉及到取指令、解碼、執(zhí)行、回寫這幾個(gè)最基礎(chǔ)的階段。最早的CPU執(zhí)行過(guò)程中是一個(gè)指令按照以上步驟依次執(zhí)行完之后,才能輪到第二條指令即指令串行執(zhí)行,很顯然這種方式對(duì)CPU各個(gè)硬件單元利用率是非常低的,為了提高CPU的性能,Intel引入了多級(jí)流水、亂序執(zhí)行等技術(shù)提升性能。一般intel cpu是5級(jí)流水線,也就是同一個(gè)cycle 可以處理5個(gè)不同操作,一些新型CPU中流水線多達(dá)15級(jí),下圖展示了一個(gè)5級(jí)流水線的狀態(tài),在7個(gè)CPU指令周期中指令1,2,3已經(jīng)執(zhí)行完成,而指令4,5也在執(zhí)行中,這也是為什么CPU要進(jìn)行指令解碼的目的:將指令操作不同資源的操作分解成不同的微指令(uops),比如ADD eax,[mem1] 就可以解碼成兩條微指令,一條是從內(nèi)存[mem1]加載數(shù)據(jù)到臨時(shí)寄存器,另外一條就是執(zhí)行運(yùn)算,這樣就可以在加載數(shù)據(jù)的時(shí)候運(yùn)算單元可以執(zhí)行另外一條指令的運(yùn)算uops,多個(gè)不同的資源單元可以并行工作。
CPU內(nèi)部還有很多種資源比如TLB、ALU、L1Cache、register、port、BTB等而且各個(gè)資源的執(zhí)行速度各不相同,有的速度快、有的速度慢,彼此之間又存在依賴關(guān)系,因此在程序運(yùn)行過(guò)程中CPU不同的資源會(huì)出現(xiàn)各種各樣的約束,本文運(yùn)用TMAM更加客觀的分析程序運(yùn)行過(guò)程中哪些內(nèi)在CPU資源出現(xiàn)瓶頸。
TMAM 即 Top-down Micro-architecture Analysis Methodology自頂向下的微架構(gòu)分析方法。這是Intel CPU 工程師歸納總結(jié)用于優(yōu)化CPU性能的方法論。TMAM 理論基礎(chǔ)就是將各類CPU各類微指令進(jìn)行歸類從大的方面先確認(rèn)可能出現(xiàn)的瓶頸,再進(jìn)一步下鉆分析找到瓶頸點(diǎn),該方法也符合我們?nèi)祟惖乃季S,從宏觀再到細(xì)節(jié),過(guò)早的關(guān)注細(xì)節(jié),往往需要花費(fèi)更多的時(shí)間。這套方法論的優(yōu)勢(shì)在于:
即使沒(méi)有硬件相關(guān)的知識(shí)也能夠基于CPU的特性優(yōu)化程序
系統(tǒng)性的消除我們對(duì)程序性能瓶頸的猜測(cè):分支預(yù)測(cè)成功率低?CPU緩存命中率低??jī)?nèi)存瓶頸?
快速的識(shí)別出在多核亂序CPU中瓶頸點(diǎn)
TMAM 評(píng)估各個(gè)指標(biāo)過(guò)程中采用兩種度量方式一種是cpu時(shí)鐘周期(cycle[6]),另外一種是CPU pipeline slot[4]。該方法中假定每個(gè)CPU 內(nèi)核每個(gè)周期pipeline都是4個(gè)slot即CPU流水線寬是4。下圖展示了各個(gè)時(shí)鐘周期四個(gè)slot的不同狀態(tài),注意只有Clockticks 4 ,cycle 利用率才是100%,其他的都是cycle stall(停頓、氣泡)。
TMAM將各種CPU資源進(jìn)行分類,通過(guò)不同的分類來(lái)識(shí)別使用這些資源的過(guò)程中存在瓶頸,先從大的方向確認(rèn)大致的瓶頸所在,然后再進(jìn)行深入分析,找到對(duì)應(yīng)的瓶頸點(diǎn)各個(gè)擊破。在TMAM中最頂層將CPU的資源操作分為四大類,接下來(lái)介紹下這幾類的含義。
Retiring表示運(yùn)行有效的uOps 的pipeline slot,即這些uOps[3]最終會(huì)退出(注意一個(gè)微指令最終結(jié)果要么被丟棄、要么退出將結(jié)果回寫到register),它可以用于評(píng)估程序?qū)PU的相對(duì)比較真實(shí)的有效率。理想情況下,所有流水線slot都應(yīng)該是"Retiring"。100% 的Retiring意味著每個(gè)周期的 uOps Retiring數(shù)將達(dá)到最大化,極致的Retiring可以增加每個(gè)周期的指令吞吐數(shù)(IPC)。需要注意的是,Retiring這一分類的占比高并不意味著沒(méi)有優(yōu)化的空間。例如retiring中Microcode assists的類別實(shí)際上是對(duì)性能有損耗的,我們需要避免這類操作。
Bad Speculation表示錯(cuò)誤預(yù)測(cè)導(dǎo)致浪費(fèi)pipeline 資源,包括由于提交最終不會(huì)retired的 uOps 以及部分slots是由于從先前的錯(cuò)誤預(yù)測(cè)中恢復(fù)而被阻塞的。由于預(yù)測(cè)錯(cuò)誤分支而浪費(fèi)的工作被歸類為"錯(cuò)誤預(yù)測(cè)"類別。例如:if、switch、while、for等都可能會(huì)產(chǎn)生bad speculation。
Front-End 職責(zé):
取指令
將指令進(jìn)行解碼成微指令
將指令分發(fā)給Back-End,每個(gè)周期最多分發(fā)4條微指令
Front-End Bound表示處理其的Front-End 的一部分slots沒(méi)法交付足夠的指令給Back-End。Front-End 作為處理器的第一個(gè)部分其核心職責(zé)就是獲取Back-End 所需的指令。在Front-End 中由預(yù)測(cè)器預(yù)測(cè)下一個(gè)需要獲取的地址,然后從內(nèi)存子系統(tǒng)中獲取對(duì)應(yīng)的緩存行,在轉(zhuǎn)換成對(duì)應(yīng)的指令,最后解碼成uOps(微指令)。Front-End Bound 意味著,會(huì)導(dǎo)致部分slot 即使Back-End 沒(méi)有阻塞也會(huì)被閑置。例如因?yàn)橹噶頲ache misses引起的阻塞是可以歸類為Front-End Bound。
Back-End 的職責(zé):
接收Front-End 提交的微指令
必要時(shí)對(duì)Front-End 提交的微指令進(jìn)行重排
從內(nèi)存中獲取對(duì)應(yīng)的指令操作數(shù)
執(zhí)行微指令、提交結(jié)果到內(nèi)存
Back-End Bound 表示部分pipeline slots 因?yàn)锽ack-End缺少一些必要的資源導(dǎo)致沒(méi)有uOps交付給Back-End。
Back-End 處理器的核心部分是通過(guò)調(diào)度器亂序地將準(zhǔn)備好的uOps分發(fā)給對(duì)應(yīng)執(zhí)行單元,一旦執(zhí)行完成,uOps將會(huì)根據(jù)程序的順序返回對(duì)應(yīng)的結(jié)果。例如:像cache-misses 引起的阻塞(停頓)或者因?yàn)槌ㄟ\(yùn)算器過(guò)載引起的停頓都可以歸為此類。此類別可以在進(jìn)行細(xì)分為兩大類:Memory-Bound 、Core Bound。
歸納總結(jié)一下就是:
Front End Bound = Bound in Instruction Fetch -> Decode (Instruction Cache, ITLB)
Back End Bound = Bound in Execute -> Commit (Example = Execute, load latency)
Bad Speculation = When pipeline incorrectly predicts execution (Example branch mispredict memory ordering nuke)
Retiring = Pipeline is retiring uops
一個(gè)微指令狀態(tài)可以按照下圖決策樹(shù)進(jìn)行歸類:
上圖中的葉子節(jié)點(diǎn),程序運(yùn)行一定時(shí)間之后各個(gè)類別都會(huì)有一個(gè)pipeline slot 的占比,只有Retiring 的才是我們所期望的結(jié)果,那么每個(gè)類別占比應(yīng)該是多少才是合理或者說(shuō)性能相對(duì)來(lái)說(shuō)是比較好,沒(méi)有必要再繼續(xù)優(yōu)化?intel 在實(shí)驗(yàn)室里根據(jù)不同的程序類型提供了一個(gè)參考的標(biāo)準(zhǔn):
只有Retiring 類別是越高越好,其他三類都是占比越低越好。如果某一個(gè)類別占比比較突出,那么它就是我們進(jìn)行優(yōu)化時(shí)重點(diǎn)關(guān)注的對(duì)象。
目前有兩個(gè)主流的性能分析工具是基于該方法論進(jìn)行分析的:Intel vtune(收費(fèi)而且還老貴~),另外一個(gè)是開(kāi)源社區(qū)的pm-tools。
有了上面的一些知識(shí)之后我們?cè)趤?lái)看下開(kāi)始的示例的各分類情況:
雖然各項(xiàng)指標(biāo)都在前面的參照表的范圍之內(nèi),但是只要retiring 沒(méi)有達(dá)到100%都還是有可優(yōu)化空間的。上圖中顯然瓶頸在Back-End。
使用Vtune或者pm-tools 工具時(shí)我們應(yīng)該關(guān)注的是除了retired之外的其他三個(gè)大分類中占比比較高,針對(duì)這些較為突出的進(jìn)行分析優(yōu)化。另外使用工具分析工程中需要關(guān)注MUX Reliability (多元分析可靠性)這個(gè)指標(biāo),它越接近1表示當(dāng)前結(jié)果可靠性越高,如果低于0.7 表示當(dāng)前分析結(jié)果不可靠,那么建議加長(zhǎng)程序運(yùn)行時(shí)間以便采集足夠的數(shù)據(jù)進(jìn)行分析。下面我們來(lái)針對(duì)三大分類進(jìn)行分析優(yōu)化。
上圖中展示了Front-End的職責(zé)即取指令(可能會(huì)根據(jù)預(yù)測(cè)提前取指令)、解碼、分發(fā)給后端pipeline, 它的性能受限于兩個(gè)方面一個(gè)是latency、bandwidth。對(duì)于latency,一般就是取指令(比如L1 ICache、iTLB未命中或解釋型編程語(yǔ)言python\java等)、decoding (一些特殊指令或者排隊(duì)問(wèn)題)導(dǎo)致延遲。當(dāng)Front-End 受限了,pipeline利用率就會(huì)降低,下圖非綠色部分表示slot沒(méi)有被使用,ClockTicks 1 的slot利用率只有50%。對(duì)于BandWidth 將它劃分成了MITE,DSB和LSD三個(gè)子類,感興趣的同學(xué)可以通過(guò)其他途徑了解下這三個(gè)子分類。
3.3.1.1 于Front-End的優(yōu)化建議:代碼盡可能減少代碼的footprint7:
C/C++可以利用編譯器的優(yōu)化選項(xiàng)來(lái)幫助優(yōu)化,比如GCC -O* 都會(huì)對(duì)footprint進(jìn)行優(yōu)化或者通過(guò)指定-fomit-frame-pointer也可以達(dá)到效果;
充分利用CPU硬件特性:宏融合(macro-fusion)
宏融合特性可以將2條指令合并成一條微指令,它能提升Front-End的吞吐。 示例:像我們通常用到的循環(huán):
所以建議循環(huán)條件中的類型采用無(wú)符號(hào)的數(shù)據(jù)類型可以使用到宏融合特性提升Front-End 吞吐量。
調(diào)整代碼布局(co-locating-hot-code):
①充分利用編譯器的PGO 特性:-fprofile-generate -fprofile-use
②可以通過(guò)__attribute__ ((hot)) __attribute__ ((code))來(lái)調(diào)整代碼在內(nèi)存中的布局,hot的代碼
在解碼階段有利于CPU進(jìn)行預(yù)取。
其他優(yōu)化選項(xiàng),可以參考:GCC優(yōu)化選項(xiàng)GCC通用屬性選項(xiàng)
分支預(yù)測(cè)優(yōu)化
① 消除分支可以減少預(yù)測(cè)的可能性能:比如小的循環(huán)可以展開(kāi)比如循環(huán)次數(shù)小于64次(可以使用GCC選項(xiàng) -funroll-loops)
② 盡量用if 代替:?,不建議使用a=b>0? x:y因?yàn)檫@個(gè)是沒(méi)法做分支預(yù)測(cè)的
③ 盡可能減少組合條件,使用單一條件比如:if(a||b) {}else{}這種代碼CPU沒(méi)法做分支預(yù)測(cè)的
④對(duì)于多case的switch,盡可能將最可能執(zhí)行的case 放在最前面
⑤ 我們可以根據(jù)其靜態(tài)預(yù)測(cè)算法投其所好,調(diào)整代碼布局,滿足以下條件:
前置條件,使條件分支后的的第一個(gè)代碼塊是最有可能被執(zhí)行的
bool is_expect = true; if(is_expect) { // 被執(zhí)行的概率高代碼盡可能放在這里 } else { // 被執(zhí)行的概率低代碼盡可能放在這里 } 后置條件,使條件分支的具有向后目標(biāo)的分支不太可能的目標(biāo) do { // 這里的代碼盡可能減少運(yùn)行 } while(conditions);
這一類別的優(yōu)化涉及到CPU Cache的使用優(yōu)化,CPU cache[14]它的存在就是為了彌補(bǔ)超高速的 CPU與DRAM之間的速度差距。CPU 中存在多級(jí)cache(register\L1\L2\L3) ,另外為了加速virtual memory address 與 physical address 之間轉(zhuǎn)換引入了TLB。
如果沒(méi)有cache,每次都到DRAM中加載指令,那這個(gè)延遲是沒(méi)法接受的。
3.3.2.1 優(yōu)化建議:
調(diào)整算法減少數(shù)據(jù)存儲(chǔ),減少前后指令數(shù)據(jù)的依賴提高指令運(yùn)行的并發(fā)度
根據(jù)cache line調(diào)整數(shù)據(jù)結(jié)構(gòu)的大小
避免L2、L3 cache偽共享
(1)合理使用緩存行對(duì)齊
CPU的緩存是彌足珍貴的,應(yīng)該盡量的提高其使用率,平常使用過(guò)程中可能存在一些誤區(qū)導(dǎo)致CPU cache有效利用率比較低。下面來(lái)看一個(gè)不適合進(jìn)行緩存行對(duì)齊的例子:
#include <stdlib.h> #define CACHE_LINE struct S1 { int r1; int r2; int r3; S1 ():r1 (1), r2 (2), r3 (3){} } CACHE_LINE; int main (int argc, char *argv[]) { // 與前面一致 }
下面這個(gè)是測(cè)試效果:
做了緩存行對(duì)齊:
#include <string.h> #include <stdio.h> #define CACHE_LINE __attribute__((aligned(64))) struct S1 { int r1; int r2; int r3; S1(): r1(1),r2(2),r3(3){} } CACHE_LINE; int main(int argc,char* argv[]) { // 與前面一致 }
測(cè)試結(jié)果:
通過(guò)對(duì)比兩個(gè)retiring 就知道,這種場(chǎng)景下沒(méi)有做cache 對(duì)齊緩存利用率高,因?yàn)樵趩尉€程中采用了緩存行導(dǎo)致cpu cache 利用率低,在上面的例子中緩存行利用率才3*4/64 = 18%。緩存行對(duì)齊使用原則:
多個(gè)線程存在同時(shí)寫一個(gè)對(duì)象、結(jié)構(gòu)體的場(chǎng)景(即存在偽共享的場(chǎng)景)
對(duì)象、結(jié)構(gòu)體過(guò)大的時(shí)候
將高頻訪問(wèn)的對(duì)象屬性盡可能的放在對(duì)象、結(jié)構(gòu)體首部
(2)偽共享
前面主要是緩存行誤用的場(chǎng)景,這里介紹下如何利用緩存行解決SMP 體系下的偽共享(false shared)。多個(gè)CPU同時(shí)對(duì)同一個(gè)緩存行的數(shù)據(jù)進(jìn)行修改,導(dǎo)致CPU cache的數(shù)據(jù)不一致也就是緩存失效問(wèn)題。為什么偽共享只發(fā)生在多線程的場(chǎng)景,而多進(jìn)程的場(chǎng)景不會(huì)有問(wèn)題?這是因?yàn)閘inux 虛擬內(nèi)存的特性,各個(gè)進(jìn)程的虛擬地址空間是相互隔離的,也就是說(shuō)在數(shù)據(jù)不進(jìn)行緩存行對(duì)齊的情況下,CPU執(zhí)行進(jìn)程1時(shí)加載的一個(gè)緩存行的數(shù)據(jù),只會(huì)屬于進(jìn)程1,而不會(huì)存在一部分是進(jìn)程1、另外一部分是進(jìn)程2。
(上圖中不同型號(hào)的L2 cache 組織形式可能不同,有的可能是每個(gè)core 獨(dú)占例如skylake)
偽共享之所以對(duì)性能影響很大,是因?yàn)樗麜?huì)導(dǎo)致原本可以并行執(zhí)行的操作,變成了并發(fā)執(zhí)行。這是高性能服務(wù)不能接受的,所以我們需要對(duì)齊進(jìn)行優(yōu)化,方法就是CPU緩存行對(duì)齊(cache line align)解決偽共享,本來(lái)就是一個(gè)以空間換取時(shí)間的方案。比如上面的代碼片段:
#define CACHE_LINE __attribute__((aligned(64))) struct S1 { int r1; int r2; int r3; S1(): r1(1),r2(2),r3(3){} } CACHE_LINE;
所以對(duì)于緩存行的使用需要根據(jù)自己的實(shí)際代碼區(qū)別對(duì)待,而不是人云亦云。
當(dāng)Back-End 刪除了微指令,就出現(xiàn)Bad Speculation,這意味著Front-End 對(duì)這些指令所作的取指令、解碼都是無(wú)用功,所以為什么說(shuō)開(kāi)發(fā)過(guò)程中應(yīng)該盡可能的避免出現(xiàn)分支或者應(yīng)該提升分支預(yù)測(cè)準(zhǔn)確度能夠提升服務(wù)的性能。雖然CPU 有BTB記錄歷史預(yù)測(cè)情況,但是這部分cache 是非常稀缺,它能緩存的數(shù)據(jù)非常有限。
分支預(yù)測(cè)在Font-End中用于加速CPU獲取指定的過(guò)程,而不是等到需要讀取指令的時(shí)候才從主存中讀取指令。Front-End可以利用分支預(yù)測(cè)提前將需要預(yù)測(cè)指令加載到L2 Cache中,這樣CPU 取指令的時(shí)候延遲就極大減小了,所以這種提前加載指令時(shí)存在誤判的情況的,所以我們應(yīng)該避免這種情況的發(fā)生,c++常用的方法就是:
在使用if的地方盡可能使用gcc的內(nèi)置分支預(yù)測(cè)特性(其他情況可以參考Front-End章節(jié))
#define likely(x) __builtin_expect(!!(x), 1) //gcc內(nèi)置函數(shù), 幫助編譯器分支優(yōu)化 #define unlikely(x) __builtin_expect(!!(x), 0) if(likely(condition)) { // 這里的代碼執(zhí)行的概率比較高 } if(unlikely(condition)) { // 這里的代碼執(zhí)行的概率比較高 } // 盡量避免遠(yuǎn)調(diào)用
避免間接跳轉(zhuǎn)或者調(diào)用
在c++中比如switch、函數(shù)指針或者虛函數(shù)在生成匯編語(yǔ)言的時(shí)候都可能存在多個(gè)跳轉(zhuǎn)目標(biāo),這個(gè)也是會(huì)影響分支預(yù)測(cè)的結(jié)果,雖然BTB可改善這些但是畢竟BTB的資源是很有限的。(intel P3的BTB 512 entry ,一些較新的CPU沒(méi)法找到相關(guān)的數(shù)據(jù))
這里我們?cè)倏聪伦铋_(kāi)始的例子,采用上面提到的優(yōu)化方法優(yōu)化完之后的評(píng)測(cè)效果如下:
g++ cache_line.cpp -o cache_line -fomit-frame-pointer; task_set -c 1 ./cache_line
耗時(shí)從原來(lái)的15s 降低到現(xiàn)在9.8s,性能提升34%:retiring 從66.9% 提升到78.2% ;Back-End bound 從31.4%降低到21.1%
[1] CPI(cycle per instruction) 平均每條指令的平均時(shí)鐘周期個(gè)數(shù)
[2] IPC (instruction per cycle) 每個(gè)CPU周期的指令吞吐數(shù)
[3] uOps 現(xiàn)代處理器每個(gè)時(shí)鐘周期至少可以譯碼 4 條指令。譯碼過(guò)程產(chǎn)生很多小片的操作,被稱作微指令(micro-ops, uOps)
[4] pipeline slot pipeline slot 表示用于處理uOps 所需要的硬件資源,TMAM中假定每個(gè) CPU core在每個(gè)時(shí)鐘周期中都有多個(gè)可用的流水線插槽。流水線的數(shù)量稱為流水線寬度。
[5] MIPS(MillionInstructions Per Second) 即每秒執(zhí)行百萬(wàn)條指令數(shù) MIPS= 1/(CPI×?xí)r鐘周期)= 主頻/CPI
[6]cycle 時(shí)鐘周期:cycle=1/主頻
[7] memory footprint 程序運(yùn)行過(guò)程中所需要的內(nèi)存大小.包括代碼段、數(shù)據(jù)段、堆、調(diào)用棧還包括用于存儲(chǔ)一些隱藏的數(shù)據(jù)比如符號(hào)表、調(diào)試的數(shù)據(jù)結(jié)構(gòu)、打開(kāi)的文件、映射到進(jìn)程空間的共享庫(kù)等。
到此,相信大家對(duì)“C/C++的性能如何優(yōu)化”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。