Unity DOTS系列之Struct Change核心机制分析

发布时间 2023-11-02 17:23:00作者: rain4414

最近DOTS发布了正式的版本, 我们来分享一下DOTS里面Struct Change机制,方便大家上手学习掌握Unity DOTS开发。

基于ArchTypeChunkEntity管理机制

  我们回顾以下ECS的内存管理核心机制,基于ArchType+Chunk的Entity管理模式。每个Entity不直接存放数据,数据全部存放到ComponentData里面。每个类型的Entity,会把它所有的ComponentData的组合在一起。每种类型的Entity都会得到”一种组合类型”,我们把它叫做ArchType。每种类型的Enitity对应一种ArchType。如果有新的类型的Entity出来,系统就会有新的一种ArchType。ArchType对应的内存块都是由Chunk统一分配,每个Chunk只会分配一种ArchType的内存块。

===============================

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机制

  当我们操作entity中的ComponentData的时候,有可能导致Struct Change,及原来的ArchType以已经不适合新的Entity,必须要产生新的ArchType来存放数据,这种我们叫做Struct Change。以下操作会导致Struct Change发生:

(1) 创建or删除一个Entity:

Unity会从当前Entity类型的ArchType里面找到一个chunk, 把第一个空闲的内存块分配分配出来。如果当前的chunk都满了,就重新向操作系统分配一个chunk内存页出来。当删除一个entity的时候,先找到内存块所在的chunk,找到内存块在chunk中的偏移位置,把最后一个entitycomponent数据复制到刚才释放的内存块中,把最后的那个内存块释放出来,供一下分配。

(2) 添加或删除一个组件数据:

当我们给entity添加or删除一个组件数据的时候,意味着前后是不同的ArchType(因为改变了组合类型)。所以要先释放原来ArchType对应的内存块,然后在新的ArchType里面再找Chunk来分配出Entity的新的ComponentData内存块。

(3) 修改了同一类型Entity共用的ShareComponent数据:

每种ArchType共用一个ShareComponent数据,如果修改了ShareComponent数据,那么意味着要创建一个新的ShareComponent数据。那么代表着当前的Entity已经不再属于当前的ArchType,既然这样,系统就要重新基于新的ArchType来给Entity重新分配内存块,回收之前的内存块。

 

Struct Change的代价与开销

Struct Change引发的开销是非常大的,所以当我们基于DOTS来开发的时候,你要能清楚的知道Struct Change的开销。Struct Change开销,除了要重新从chunk里面分配内存块,复制数据以外,还有同步点的开销,同步点你可以理解为一个锁,当发生了Strcut Change的时候,为了保证正确性,系统会生成一个同步点,这样其它的要使用这个数据的线程都会被挂起,直到Struct Change操作完成。

   Struct Change还会导致之前系统里面的组件引用失效,所以当发生Struct Change的时候,还要重新更新引用数据,保证后面数据的正确。从上面来看Struct Change开销确实很大,特别是每次修改还要获取同步点,同步点会导致系统的吞吐量下降,我们可以考虑把所有的Struct Change延后一起发生,这样可以只请求一个同步点的基础上把所有的Struct Change全部处理掉。