unity要学ecs吗_ECS的泛泛之谈

18f37fff0e81ff302e90fe8e5d573f1a.png

这篇文章将带着你从设计出发重新发现ECS。

注意:此篇为泛泛之谈,不涉及具体实现。


从Abstract说起

  1. 从”是”到”能”再到”有”

对对象的抽象是整理代码的要点,继承是一种比较古老并常见的抽象,其描述了一个对象"是"什么,其中包含了对象拥有的属性和对象拥有的方法,在简单情况下,继承是一种非常易用易懂的抽象,然而在更复杂的情况下,继承引入的的问题渐渐浮现出来,使得它不再那么易用。

以下列举几个例子:

  • 深层次继承树(要理解一个类,需要往上翻看非常多的类)。
  • 强耦合(修改基类会影响到整棵子继承树)。
  • 菱形继承(祖父的数据重复,方法产生二义性)。
  • 繁重的父类(子类的方法被不断提取到父类,导致父类过度膨胀,某 UE4)。
  • 而这些问题又相互影响产生恶性循环,使得项目的后期开发和优化变得无比困难。

4f103d51f2dedda7ddcb1617e04b554f.png
万恶之源

于是,大家便尝试简化模型,并描述了一种叫做接口的抽象,其描述了一个对象"能"干什么,其中包含了对象拥有的方法(不再包含数据),接口隐藏了对象的大部分细节,使得对象变成一个黑箱,且展平了类结构(不再是树状),然而接口(这里指运行时接口而非泛型)作为一种非常高层次的抽象,这种抽象层次似乎有时会过高,导致CPU更难以理解代码,这点在稍后会讨论到。

类似的,在游戏开发中,面对大量的对象种类,大家又描述了一种组件的抽象,如 UE4 中的 Actor Component 模型和 Unity 中的 Entity Component 模型,其描述了一个对象"有"什么部分,其中对象本身不再拥有代码或数据(但其实 Unity 和 UE4 之类的并没有做到这么纯粹,对象本身依然带有大量"基础"功能,这导致了代码量和内存占用的双重膨胀)。组件的方式带来了优越的动态性,对象的状态完全由其拥有的组件决定(同样,一般没这么纯粹),甚至可以动态的改变。并且这让我们可以排列组合以少量的组件组合出巨量的对象(当然,有效组合往往没那么多)。有趣的是,从展平对象结构的角度看起来和组件和接口有着微妙的相似性。不过这种抽象也带来出了一些歧义性,接下来将讨论这一点。

64b4e01e9ca93a6d927d289dd5ba65a4.png
组合

2. “有”和”能”和实现

在组件模型中,对象由组件组成,所以其行为也由组件主导,例如一个对象拥有[Movement] 和 [Location],则我们可以认为它能够移动,这在整体上是十分和谐自然的,但当我们仔细考量,这个"能"是由于什么呢,是因为 [Movement]吗,是因为[Location]吗,还是同时因为 [Movement] 和 [Location]?当然是同时(这里便揭示出了组件和接口的展平对象方式是正交的),那移动的逻辑放到哪呢?答案是放在这个“切片“上。但在实际项目中会看到把逻辑放在 [Movement] 上的做法,这两种方式都是可取的,后一种拥有较为简单的实现并被广泛采用,而前一种拥有更精准的语义,更好的抽象(后一种种方式中 [Movement] 去访问并修改了 [Location] 的数据,这破坏了一定的封闭性,且形成了耦合,当然这种耦合也有一定的好处,如避免只添加了 [Movement] 这种无意义的情况发生)。


从Cache说起

  1. Cache Miss

Cache(Cache Memory)作为储存器子系统的组成部分,存放着程序经常使用的指令和数据,是为了缓解访问慢设备的延时而预读的 Buffer,例如 CPU L1/L2/L3 Cache 作为 DDR 内存 IO 的 Cache,而 DDR 内存作为磁盘 IO 的 Cache。当计算需要读取数据的时候,通常从最快得缓存开始依次向下查找,并递归的读取。预读就是用来减少下一次读取的查找层数(每一层的延迟有数量级的差距)的技术。相应的,预读的预测失败的时候将会有非常高的代价,这种情况被称为 Cache Miss。在大部分的情况下,在现代 CPU 的频率带来的运算力下, Cache Miss 比数学运算更容易成为程序的性能瓶颈,且在代码中的表现比较隐晦。这使得一味的讨论复杂度O(n)不再适用,因为现在效率=数据+代码,最常见的例子就是在数据量小的情况下遍历数组会比 (Hash)Map 快上很多,这也是Java或C#这类语言的效率陷阱.。

42cea53e1de2672b8e54930d490318c4.png
从上到下进行查找

2. Avoid Cache Miss

避免 Cache Miss 的方案当然就是去讨好预读。而一般预读的策略为线性预读,即我们应该尽量的保证数据读写的连续性,从逆向思维出发,则需了解会打断数据连续性的情形。简单的列举几个:遍历大结构体的数组(却只访问少数成员),操作对象引用(OOP),操作数组的顺序不够连续(比如实现得不好的 hash 表),etc。综上所述,避免Cache Miss的主要考量就是尽量使用数组,尽量分割属性(SOA),尽量连续的进行处理。(在 GPU 编程中存在大量实例)

9d698fd635057f136f2f844f52db3ca1.png
此时达到理论最高效率

3. More than Data

前面提到过 Cache 存放着程序经常使用的指令和数据,现代 CPU 在数据 IO 的时候并不会完全的挂起,而是会利用空闲的运算力继续执行后续的指令,且指令也是一种数据,这意味着我们不光要照顾数据的连续性,还需要考虑到指令的连续性,那么什么情况会破坏指令的连续性呢?可能是函数指针(虚函数的调用,回调等),循环超长代码块等。特别是函数指针在 IO 期间,CPU 无事可做,于是在需要高性能的情形下,应该尽量避免虚函数

4. Allocation

对于数据而言,还有一个重要的问题就是分配内存。在应用中,不管是分配还是释放都是十分消耗性能的操作,前者可能产生碎片,而后者,(考虑 GC)可能带来停顿,(考虑 RC)带来析构血崩,(考虑手动)也可能带来危险和脑力负担,所以一般对于高频分配的部分,会预先分配大块内存用来管理(一般称作池化)。


从 Thread 说起

  1. Multithread

随着处理器核心的发展速度减缓,为了进一步提升处理器的性能,堆叠核心成为了新的出路,甚至现在的处理器没个四核都不好意思见人,其中堆叠核心的巅峰就是 GPU,上千个核心带来了疯狂的数字处理能力,被广泛运用于 AI 和图形领域。而这在游戏之类的高性能软件中,为了充分利用 CPU 的算力,程序设计成多线程运行也是非常必要的。

2. Race Condition 和 Data Race

不幸的是多线程很多时候不是免费的性能,并不是所有情况都像异步读文件那么简单,在开发过程中,很多地方都可能会有 Race 的发生。同步性问题非常的恶心,因为通常其不会即时造成崩溃之类的错误,而是会积累错误,等到错误爆发,缘由已经很难查询。所以编码的时候就必须要小心翼翼,其中 Race Condition 主要需要我们保证整体操作的原子性,一般的解决方案是一把大锁。Data Race 则更加复杂,触发Data Race的条件可以归纳为:

1,同一个位置的对象。

2,被两个并行的线程操作。

3,两个线程并非都是读。

4,不是原子操作。

1134d837845c9151c61acb09e21eee75.png

只有当这四个条件同时成立的时候,Data Race 才会发生,所以为了避免它的发生,我们需要破坏掉其中的一个或多个条件。对于条件4,可以使用原子操作破坏,然而原子操作的复杂性颇高,实际应用中常用于实现底层库(无锁队列,线程池之类的)。而要破坏条件1、3,则是避免可变共享,完全进行拷贝(如erlang)。剩下条件2就是避免硬碰硬,在可能发生 Data Race 的时候直接放弃并行。但总得来说最重要的还是,要避免它的发生,一定要对这些条件足够敏感以预防遗漏,在这里通常封装就起了反作用,因为黑箱之内我们无法知道会发生什么。而此时相对于 OOP 的黑箱,函数式的纯粹(纯原子性)便能体现出它在并行上天生的优势,所以卡神推荐在 C++ 里也尽量使用函数式的思想来进行编码


交汇之地 - 三相之力!

  1. Component 与 System

之前说到组件模式的时候,我们列举了两种方式来存放实现组件功能的代码,而使用“管理器”实现的方式,拥有更精准的语义和更好的抽象,组件之间被彻底解耦,而这个“管理器”我们称之为系统(System)。即系统负责管理特定的组件的组合,而组件则不再负责逻辑。接下来分别讨论这两个部分。

825c9113e0538a37ea208768a5c9e579.png
筛选对应的实体

2. System

对象耦合于接口,而这里系统则耦合于对象。这意味着组件不变的情况下,系统的任何修改都不会对程序的其余部分造成影响。这给代码带来了出色的内聚性,让 culling 和 plugin 都变得更轻松,并且系统本身拥有很好的纯度,我们完全可以把系统看做是”输入上一帧的数据,输出下一帧的数据“。也就是系统本身贴合了函数式的思想,根据前面的叙述,函数式在并行上有天生的优势,这在系统上也体现了出来:系统负责管理组件的信息是透明的,于是我们对系统对组件的读写便一目了然 - 注意结构体之间没有任何依赖,系统与系统之间的冲突也一目了然。更进一步,在通常情况下,系统是一个白箱,运作系统的代码将不会经过虚函数,不管是效率还是可测试性都是极好的。甚至对于系统的执行调度也完全暴露了出来,这在实现网络同步之类的框架的时候能提供很大的便捷性。

3. Component 与 Entity

对于对象本身,其实已经不必要承载多少信息了,激进一点说,对象甚至只是一个唯一的ID,用于和其他对象区分而已,这让我们有机会去除那些"基础"功能的依赖(例如 Transform),使得内存和代码进一步压缩。而组件不包含逻辑,就只有数据,作为一个大的对象的分割的属性,通常为小结构体。对于每一种组件,我们可以使用紧密的数组来储存它,而这也意味着我们可以轻松的池化这个数组。在系统管理组件的时候,并不关心特定 Entity,而是在组件数据的切片上批量的连续的进行处理,这在理想情况下能大大的减少 Cache Miss 的情况。作为额外的好处,纯数据的组件对序列化,表格化有着极强的适应性,毕竟对象天生就是一个填着组件的表格,对网络、编辑、存档等都十分的友好。(这里也可以引入很多数据库相关的知识)

4. ECS

至此,我们重新发现了 ECS,并详细阐述了它的好处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

条令考试小程序辅助器_计算机一级考试干货!

计算机一级考试干货一年两度的计算机等级考试就要在2019年3月30-31日期间开始啦!为了更好的让同学们了解考试的大体内容,我们已整理如下内容,可以供大家参考。同时,希望大家认真备考,争取都一次性过哦!考试…

android文件系统只读,android

>>#1 票数:408最近,我扎根了Droid X,一切似乎都运转良好。 我对build.prop进行了一些更改,当我执行adb push build.prop /system/ ,出现以下错误: failed to copy c:\\build.prop to /system//build.…

燃气灶电气线路图及原理_一位造价大神的电气工程造价知识整理笔记_深圳电气造价预算培训要多少钱...

电气设备安装工程是建设工程中的一种常见的、重要的设备安装工程。电气设备安装工程计价涉及到许多电气工程专业知识。一、电气设备安装工程的组成一般的电气设备安装工程是以接受电能,经变换、分配电能,到使用电能或从接受电能经过分配到用电设备所形成…

荣耀es升级鸿蒙,华为手机明年全部升级鸿蒙系统 所有自研设备换OS

近日,华为举行 HarmonyOS 2.0 手机开发者 Beta 活动,现场正式发布了 HarmonyOS 2.0 手机开发者 Beta 版本。华为消费者业务软件部副总裁杨海松在接受媒体采访表示,到今天为止,参与鸿蒙开发项目的开发者数量超过 10 万,…

signature=08653706deda7871a1f662eae4d2b33e,The nature of nuclear Halpha emission in LINERs

摘要:To get insight in the nature of the ionized gas in the nuclear region of LINERs we have performed a study of HST Halpha imaging of 32 LINERs. The main conclusion from this analysis is that for the large majority of LINERs (84%) an unresolve…

腐蚀国内稳定服务器_工控机箱和服务器机箱区别在哪里

服务器机箱必须能够装进机柜,一个标准机柜的宽度是19英寸482.6mm,所以机箱的宽度是几乎固定的,一般是17英寸左右,两侧再安装把手和轨道。机箱高度也有规定,用U(Unit)做单位,1U是44.5mm,机箱高度…

signature=99daf37ca32015c39987d04abe5a559d,合肥2015年7月4日至2015年7月16日交通违章查询...

备注:查询时按ctrlf,方便快速查找。小型汽车(蓝牌)藏AQ1103 川A2ZH95 川A2ZL77 川A4RL16 川AB9B03 川AF8R10川AG28F8 川RS5355 鄂A51602 鄂A6NW35 鄂A721K6 鄂A87Q05鄂A8XQ78 鄂A9DU50 鄂AH07W0 鄂AJ1V12 鄂AK08L5 鄂AM02D6鄂AM5488 鄂AN1A91 鄂AP5Y77 鄂…

hive不在同一台机 hue_环境篇:呕心沥血@CDH线上调优

环境篇:呕心沥血CDH线上调优为什么出这篇文章?近期有很多公司开始引入大数据,由于各方资源有限,并不能合理分配服务器资源,和服务器选型,小叶这里将工作中的总结出来,给新入行的小伙伴带个方向&…

网页制作的基本语言html,网页制作基础语言HTML.ppt

网页制作基础语言HTML2. 内嵌音频播放插件 通过内嵌音频播放插件可以在浏览器中出现控制面板,也可作为背景音乐。其格式为: 【例2-27】 3. IE中的背景音乐 可以插入背景音乐格式,不过只有在IE浏览器中才可以听到。其格式为: 播放次…

coji 编程机器人_WowWee编程机器人怎么样 WowWee编程机器人使用测评

现在社会已经互联网化,很多小孩的补习班里面也开始出现编程特色补习班,也出现了编程玩具,下面乐海购的编辑就来说说:WowWee编程机器人怎么样WowWee编程机器人使用测评。WowWee编程机器人怎么样我家宝宝对于WowWee编程机器人这样的…

宿松县事业单位计算机基础知识,计算机基础知识试题(事业单位考试)

原标题:计算机基础知识试题(事业单位考试)1、世界上首先实现存储程序的电子数字计算机是____. A.ENIAC B.UNIVAC C.EDVAC D.EDSAC2、计算机科学的奠基人是____.A.查尔斯。巴贝奇 B.图灵 C.阿塔诺索夫 D.冯。诺依曼2、世界上首次提出存储程序计算机体系结构的是____.…

数组添加进formdata_javascript – FormData追加数组中的项目

public List Regions { get; set; }在名为News.An Region Model的模型中public class Region{public int Id { get; set; }public string Name { get; set; }public static Region Parse(DataRow row){return new Region{Id Database.GetInteger(row["Id"]),Name D…

perl 安装html,centos perl 安装HTML-Parser时报错

新装了centos7,安装perl模块时,发现系统自带的perl很多基础的包都没安装(是基础perl的阉割版本,甚至失去了很多重要的基础包 http://www.perlmonks.org/?node_id486526),比如 ExtUtils::MakeMaker,ExtUtils::ParseXS,ExtUtils::E…

limit实现原理 mysql_解读数据库:深入分析MySQL中事务以及MVCC的实现原理

什么是事务事务(Transaction)是由一系列对数据库中的数据进行访问与更新的操作所组成的一个程序执行单元。在同一个事务中所进行的操作,要么都成功,要么就什么都不做。理想中的事务必须满足四大特性,这就是大名鼎鼎的ACID。事务的…

计算机科学与技术排名中澳合作,计算机科学与技术(中澳合作)2018培养方案.docx...

计算机科学与技术(中澳合作办学)(专业代码:8053)—、专业简介计算机科学与技术专业(中澳合作办学)由我校与澳大利亚国立大学联合培养,有22模式 (两年国内,两年澳大利亚国立大学)和40模式(四年都在国内)可供学生自由选择。本专业 在全国多个省…

c# 审批流引擎_小熊OA:流程引擎才能真正起到管理价值!

首先说说什么是流程管理。流程作为企业运作的基础,不同部门、不同客户和供应商都需要流程来进行协同运作,以流程带动信息、物资和资金在企业内部无障碍地流转。流程管理是一种以业务流程为中心,以提高组织业务绩效为目的的系统化方法。它是一…

在计算机桌面上添加小工具日历,实用桌面小工具时钟日历在win7中的添加方法...

我们在win7系统的使用中,小伙伴们都是知道的系统可以直接选择安装不同的小工具在电脑中使用,比如电脑中的时钟日历等都是可以直接安装在桌面来使用的牡丹石有小伙伴对于时钟日历不知道是在哪里添加到桌面的,对于这个疑问今天小编就来跟大家分…

html 为什么ul不撑开,给li设置float浮动属性之后,无法撑开外层ul的问题。

以下面代码为例,其实有好几种解决方法,我用的这种并不是最简单的。给li设置float浮动属性之后,无法撑开外层ul的问题。ul{border: 1px solid #000;width: 200px;height: auto;margin-top: 100px;}li{float: left;list-style: none;margin-lef…

pb11 打印 自定义纸张_pb自定义纸张(连续走纸)

Pb中定制打印页长实现连续打印pb与打印机的通讯使用ASCII码进行,其中标准ASCII码包括可打印字符及非打印字符(控制码),打印机使用控制码来定制打印机。大多数打印机指令使用控制码escape作为其指令序列的第一个序列码。设置换行量(行距)1/8 英寸ASCII码 …

计算机黑屏无法启动,电脑黑屏无法启动怎么办

有网友说自己的电脑黑屏无法启动,具体现象就是开机以后,主机电源灯亮,但显示没有任何显示。那么电脑黑屏无法启动的原因很多,下面小编就给大家分享下电脑不能开机黑屏的解决方法。静电影响:1、当出现不能开机的问题时&…