Unity DOTS系列之Struct Change核心機(jī)制分析
?最近DOTS發(fā)布了正式的版本, 我們來分享一下DOTS里面Struct Change機(jī)制,方便大家上手學(xué)習(xí)掌握Unity DOTS開發(fā)。
基于ArchType與Chunk的Entity管理機(jī)制
??我們回顧以下ECS的內(nèi)存管理核心機(jī)制,基于ArchType+Chunk的Entity管理模式。每個(gè)Entity不直接存放數(shù)據(jù),數(shù)據(jù)全部存放到ComponentData里面。每個(gè)類型的Entity,會(huì)把它所有的ComponentData的組合在一起。每種類型的Entity都會(huì)得到”一種組合類型”,我們把它叫做ArchType。每種類型的Enitity對(duì)應(yīng)一種ArchType。如果有新的類型的Entity出來,系統(tǒng)就會(huì)有新的一種ArchType。ArchType對(duì)應(yīng)的內(nèi)存塊都是由Chunk統(tǒng)一分配,每個(gè)Chunk只會(huì)分配一種ArchType的內(nèi)存塊。 =============================== ArchType1: chunk1【e1(c1c2),e2(c1c2),e3(c1c2)】 chunk2【e4(c1c2),e5(c1c2),e6(c1c2)】 ... ====================== ArchType2: chunk1【e1(c3c4),e2(c3c4),e3(c3c4)】 chunk2【e4(c3c4),e5(c3c4),e6(c3c4)】 ... =============================== ArchType3: chunk1【e1(c5c6),e2(c5c6),e3(c5c6)】 chunk2【e3(c5c6),e4(c5c6),e5(c5c6)】 ... =============================== ?? Struct Change機(jī)制
??當(dāng)我們操作entity中的ComponentData的時(shí)候,有可能導(dǎo)致Struct Change,及原來的ArchType以已經(jīng)不適合新的Entity了,必須要產(chǎn)生新的ArchType來存放數(shù)據(jù),這種我們叫做Struct Change。以下操作會(huì)導(dǎo)致Struct Change發(fā)生: (1)?創(chuàng)建or刪除一個(gè)Entity: Unity會(huì)從當(dāng)前Entity類型的ArchType里面找到一個(gè)chunk, 把第一個(gè)空閑的內(nèi)存塊分配分配出來。如果當(dāng)前的chunk都滿了,就重新向操作系統(tǒng)分配一個(gè)chunk內(nèi)存頁出來。當(dāng)刪除一個(gè)entity的時(shí)候,先找到內(nèi)存塊所在的chunk,找到內(nèi)存塊在chunk中的偏移位置,把最后一個(gè)entity的component數(shù)據(jù)復(fù)制到剛才釋放的內(nèi)存塊中,把最后的那個(gè)內(nèi)存塊釋放出來,供一下分配。 (2)?添加或刪除一個(gè)組件數(shù)據(jù): 當(dāng)我們給entity添加or刪除一個(gè)組件數(shù)據(jù)的時(shí)候,意味著前后是不同的ArchType(因?yàn)楦淖兞私M合類型)。所以要先釋放原來ArchType對(duì)應(yīng)的內(nèi)存塊,然后在新的ArchType里面再找Chunk來分配出Entity的新的ComponentData內(nèi)存塊。 (3)?修改了同一類型Entity共用的ShareComponent數(shù)據(jù): 每種ArchType共用一個(gè)ShareComponent數(shù)據(jù),如果修改了ShareComponent數(shù)據(jù),那么意味著要?jiǎng)?chuàng)建一個(gè)新的ShareComponent數(shù)據(jù)。那么代表著當(dāng)前的Entity已經(jīng)不再屬于當(dāng)前的ArchType了,既然這樣,系統(tǒng)就要重新基于新的ArchType來給Entity重新分配內(nèi)存塊,回收之前的內(nèi)存塊。 ? Struct Change的代價(jià)與開銷
Struct Change引發(fā)的開銷是非常大的,所以當(dāng)我們基于DOTS來開發(fā)的時(shí)候,你要能清楚的知道Struct Change的開銷。Struct Change開銷,除了要重新從chunk里面分配內(nèi)存塊,復(fù)制數(shù)據(jù)以外,還有同步點(diǎn)的開銷,同步點(diǎn)你可以理解為一個(gè)鎖,當(dāng)發(fā)生了Strcut Change的時(shí)候,為了保證正確性,系統(tǒng)會(huì)生成一個(gè)同步點(diǎn),這樣其它的要使用這個(gè)數(shù)據(jù)的線程都會(huì)被掛起,直到Struct Change操作完成。 ???Struct Change還會(huì)導(dǎo)致之前系統(tǒng)里面的組件引用失效,所以當(dāng)發(fā)生Struct Change的時(shí)候,還要重新更新引用數(shù)據(jù),保證后面數(shù)據(jù)的正確。從上面來看Struct Change開銷確實(shí)很大,特別是每次修改還要獲取同步點(diǎn),同步點(diǎn)會(huì)導(dǎo)致系統(tǒng)的吞吐量下降,我們可以考慮把所有的Struct Change延后一起發(fā)生,這樣可以只請(qǐng)求一個(gè)同步點(diǎn)的基礎(chǔ)上把所有的Struct Change全部處理掉。