官方文档:https://docs.unity3d.com/Packages/com.unity.entities@0.9/manual/ecs_core.html

视频资料:UUG Online直播回放:DOTS从原理到应用-雨松MOMO

当前版本:Unity2019.3.0,需要在Package Manager中安装Entities。

CPU与缓存(Cache)

首先我们先来了解一下CPU读取数据时的操作,首先CPU会先从自己的缓存中去查找,如下图,有L1/ L2/ L3三级缓存,若缓存中没有找到需要的数据,则会去内存中查找(我们称之为Cache Miss),CPU读取到内存数据后就会将新数据存放在缓存当中。CPU访问内存的速度会比访问L1 Cache的速度慢100倍,因此提高缓存命中率(Cache Hit),避免Cache Miss会大大提高性能。因此我们应该尽量使用数组,尽量分割属性(SOA),尽量连续的进行处理。

这也使得一味的讨论复杂度O(n)不再适用,因为现在效率=数据+代码,最常见的例子就是在数据量小的情况下遍历数组会比 (Hash)Map 快上很多

缓存行

缓存又由若干个缓存行(cache line)组成,每个缓存行大概占64字节。

假设我们现在想要旋转并移动场景中的一个物体,那么我们会修改它的Position(Vector3数据,3个float)和Rotating(Quaternion,4个float),其中1个float占4字节,那么一共占28字节,那么在这个缓存行就会有36个字节是完全浪费的,甚至我们可能只改了Position的x的值,这样浪费的就更多。若有上千个这样的方块,那么我们缓存中可能就会存在超50%的内存垃圾,从而导致缓存命中率大大的降低。

问题的引出

了解了上面的知识之后,我们再看回Unity,在传统模式下,我们在场景中创建一个Cube,上面会有Transform,MeshRenderer,Collider等组件,而这些组件在内存中的排放都是无序的,这就会降低我们的缓存命中率。

除此以外,像我们前面说到的旋转移动方块,我们只用到了Position和Rotating两个属性,但是使用的时候整个Transform都会被加到缓存当中,而Transform中有很多我们不需要的属性占用了不少的缓存空间,同样的降低了我们的缓存命中率。

使用ECS就可以解决上述的这些问题,从而提高性能。

ECS概念

ECS即Entity Component System,是Unity的一种框架,我们可以把Entity看做是一个唯一id,Component看做是数据,其本质是一个存放数据的Struct,一个Entity上可以拥有多个Component,但是Component中不会有任何逻辑处理。而System则可以根据Entities索引读取对应的Component,对Component中的数据进行处理。

如上图,有三个Entity,Entity A,Entity B,Entity C,与它们相关联的Translation,Rotation,LocalToWorld和Renderer这些都是Component。同时还有一个System,用来将Translation和Rotation两个Component内的值相乘并赋予LocalToWorld。

我们可以设置System需要Renderer Component,那么Entity C将被该System忽略。或者设置System处理的Entity不能有Renderer Component,那么Entity A和Entity B将被忽略。

Archetypes

不同的Component可以有多种不同的组合,每种组合我们即称之为Archetype。例如下图,Entity A和Entity B的Component的组合是相同的,所以他们都属于Archetype M,而Entity C由于少了Renderer,所以是另一种组合,属于Archetype N。

由于可以在运行时给Entity动态的添加或删除Component,若我们将Entity A的Renderder删除,则此时Entity A会隶属于Archetype N,若再删除Rotation,则剩下的Component组合和Archetype M,Archetype N都不相同,就好隶属于一个新的Archetype。

Memory Chunks

ECS会根据Archetype来进行分配内存,每个内存块我们称之为Chunk,ECS会将符合Chunk对应Archetype的Entity放在该Chunk当中。一个Chunk中,内存地址是连续的,大小固定为16KB。若Chunk装满了,则会生成一个新的Chunk用来存储新生成的且Archetype符合的Entity。

由于我们动态的添加或删除Entity的Component,会导致其Archetype变化,因此ECS也会改变其Chunk,放到与之对应Chunk中。

可能描述的不太好,我们来看示例图。下图中有三种Archetype,分别对应三种Component组合。每个Archetype都会有对应的Chunk用来存储对应的Entity。若Chunk存储满了,就会在对应Archetype下新生成一个Chunk。

这样的设计理念使Archetypes and Chunks是一对多的关系,同时若给定一个Component组合,我们要找到所有对应的Entity,只需要搜索现有的archetype即可,而不需要遍历所有的Entity。

ECS不支持使用特殊的排序来将Entity存储进Chunk中,若有一个Entity被创建或者被改变,使其隶属于一个新的Archetype时,ECS会将其存储在该Archetype下第一个还有空间的Chunk中。若有一个Entity被从Chunk中移除,ECS则会把该Chunk中最后一个Entity与其对应的Component移到这个空缺的位置中。

举例

关于Entity,Component,Archetype和Chunk的关系,我们在此举个简单的例子。假设我们有两个Component:C1和C2,然后我们生成五个Entity,其对应的Component分别为:E1(C1,C2),E2(C1),E3(C1,C2),E4(C1,C2),E5(C2)。由于组合分别有C1,C2,C1C2三种,所以会有三个Archetype,假设我们一个Chunk只能存储两个Entity。那么最终结果为:

Archetype1:Chunk1 [ E1(C1,C2),E3(C1,C2) ]  -> Chunk2 [ E4(C1,C2) ]

Archetype2:Chunk1 [ E2(C1) ]

Archetype3:Chunk1 [ E5(C2) ]

结合到我们前面有关缓存的知识,若我们要移动选择一个方块,可能会写两个Component,MoveComponent存x和y两个float(假设只前后左右移动),RotateComponent只存y一个float(假设只旋转y轴),这样我们的方块就只会有3个float,占用12个字节。若有多个相同的方块,由于都有上述两个Component,也就是属于同一种Archetype,因此内存也是连续的。那么在一个64字节的缓存行中,我们可以放下5个这样的方块数据,只有4字节是浪费的,就会大大降低Cache Miss。

ECS的简单入门(一):概念相关推荐

  1. ECS的简单入门(四):System

    概念 在System中,我们就可以对Component中的数据进行处理了,例如我们每帧改变Translation Component的值,就可以使Entity移动起来. ECS为我们提供了以下几种Sy ...

  2. ECS的简单入门(三):Component

    Component概念 ECS中的Component和我们以往MonoBehaviour的Component不同,ECS的Component中,只用于存放数据,并不会有行为.操纵数据的行为我们交给了S ...

  3. ECS的简单入门(六):传统GameObject模式转换到ECS模式

    在前面的介绍中,我们都是通过代码来实现物体在场景中的显示,例如我们要显示一个Cube,通过创建Entity,添加RenderMesh等Component,设置相应的Material和Mesh值即可.这 ...

  4. 音视频基本概念和FFmpeg的简单入门(新手友好+FFmpeg资料分享)

    音视频基本概念和FFmpeg的简单入门 基本概念 容器/文件(Conainer/File): 即特定格式的多媒体文件,一般来说一个视频文件是由视频,音频,字幕等按特定的格式/规则组合到一起的,常见如: ...

  5. oracle简单入门(全面)

    oracle简单入门 oracle概念 oracle是一个关系型数据库,它有一个大的数据库,大的数据库下由一个或多个实例组成,每个实例由后台进程和逻辑数据组成,一个实例下可以创建多个表空间和多个用户, ...

  6. python如何读取mat文件可视化_python Matplotlib数据可视化(1):简单入门

    1 matplot入门指南 matplotlib是Python科学计算中使用最多的一个可视化库,功能丰富,提供了非常多的可视化方案,基本能够满足各种场景下的数据可视化需求.但功能丰富从另一方面来说也意 ...

  7. Python 简单入门指北(二)

    Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...

  8. 分布式锁简单入门以及三种实现方式介绍(滴滴)

    很多小伙伴在学习Java的时候,总是感觉Java多线程在实际的业务中很少使用,以至于不会花太多的时间去学习,技术债不断累积!等到了一定程度的时候对于与Java多线程相关的东西就很难理解,今天需要探讨的 ...

  9. CQRS简单入门(Golang)

    一.简单入门之入门 CQRS/ES和领域驱动设计更搭,故整体分层沿用经典的DDD四层.其实要实现的功能概要很简单,如下图. 基础框架选择了https://github.com/looplab/even ...

最新文章

  1. 选中断还是轮询方式?深究其中的区别
  2. $‘\r‘: command not found解决方法
  3. 给演讲增色的10种简单方法
  4. IDEA2021快捷键windows
  5. python时间序列预测报错_python如何做时间序列
  6. network packet
  7. 腾讯手游如何提早揭露游戏外挂风险?
  8. 分布式系统中的幂等设计
  9. 阿里 AI「一对多」挑战人类律师;谷歌或将推出自研手机和电脑芯片;JavaScript诞生25周年|极客头条...
  10. vue 一周日历展示,上一周下一周展示。
  11. 51单片机之串口通信详解及代码示例
  12. 猴子吃桃 java_【Java编程基本功】(六)猴子吃桃,运动员的出场顺序,abc三个数排序...
  13. 如何进行用户画像分析
  14. roce流量抓包 linux,roce性能测试方法
  15. 超详细open vn搭建之Linux亲测可用
  16. 女生转行软件测试难吗?
  17. 洛谷P2486 染色
  18. 力扣---LeetCode20. 有效的括号(栈)
  19. matlab交流电路仿真,【2017年整理】7基于MATLAB的交流电机仿真.ppt
  20. php命名空间非限定名称,PHP命名空间 - osc_nnp3dgfb的个人空间 - OSCHINA - 中文开源技术交流社区...

热门文章

  1. 微型计算机ccc认证依据,3C检测规定
  2. 如何将PDF文件转换成TXT文档
  3. matplotlib画布中属性设置常用函数及其说明
  4. ubuntu清理缓存
  5. 奇虎360 2013校园招聘笔试题
  6. 报错解决——RuntimeError: The size of tensor a (4) must match the size of tensor b (3) at non-singleton
  7. 【论文精读】Pairwise learning for medical image segmentation
  8. BootstrapTable去掉表格与分页的空白
  9. 【问题解决】mac微信截图只能截到空白页面问题
  10. 10.发布者Publisher的编程实现